किसी फ़ाइल में एक पंक्ति को केवल तभी लागू करना जब वह पहले से मौजूद न हो


157

मुझे एक कॉन्फ़िग फ़ाइल के अंत में निम्न पंक्ति जोड़ने की आवश्यकता है:

include "/configs/projectname.conf"

नामक एक फ़ाइल के लिए lighttpd.conf

मैं sedयह करने के लिए उपयोग कर रहा हूँ , लेकिन मैं कैसे काम नहीं कर सकता।

यदि लाइन पहले से मौजूद नहीं है तो मैं इसे कैसे सम्मिलित करूंगा?


यदि आप एक आईएनआई-फाइल को संपादित करने की कोशिश कर रहे हैं, तो उपकरण crudiniएक अच्छा विकल्प हो सकता है (लेकिन अभी तक lighthttpd के लिए नहीं)
rubo77

जवाबों:


292

बस इसे सरल रखें :)

grep + इको पर्याप्त होना चाहिए:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
  • -q शांत रहें
  • -x पूरी लाइन का मिलान करें
  • -F पैटर्न एक सादे स्ट्रिंग है
  • https://linux.die.net/man/1/grep

संपादित करें: @cerin और @ thijs-wouters सुझावों को शामिल किया गया


2
मैं -re स्विच को grep में आउटपुट को दबाने के लिए जोड़ दूंगा: grep -vq ...
डेव किर्बी

1
FYI करें, -v और && ट्रिक करने के लिए नहीं दिखता है, जबकि || बिना -v काम करता है।
bPizzi

3
यह केवल बहुत ही सरल लाइनों के लिए काम करता है। अन्यथा, grep पैटर्न के रूप में आपकी लाइन में अधिकांश गैर-अल्फा वर्णों की व्याख्या करता है, जिससे यह फ़ाइल में रेखा का पता नहीं लगा सकता है।
सेरिन जूल

2
सुंदर समाधान। यह निश्चित रूप से अधिक जटिल अभिव्यक्तियों को ट्रिगर करने के लिए भी काम करता है। मेरा एक विन्यास फाइल में बहुस्तरीय हेरेडोक echoको ट्रिगर करने के लिए उपयोग करता है cat
एरिक एल।

2
-sजब फ़ाइल मौजूद नहीं है, तो त्रुटियों को अनदेखा करने के लिए जोड़ें , उस लाइन के साथ एक नई फ़ाइल बना रही है।
फ्रैंक

78

यह एक साफ, पठनीय और पुन: प्रयोज्य समाधान का उपयोग करेगा grepऔर echoएक फ़ाइल में एक पंक्ति को जोड़ने के लिए केवल अगर यह पहले से मौजूद नहीं है:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

यदि आपको पूरी लाइन उपयोग से मेल खाना है grep -xqF

-sजब फ़ाइल मौजूद नहीं है, तो त्रुटियों को अनदेखा करने के लिए जोड़ें , उस लाइन के साथ एक नई फ़ाइल बना रही है।


5
यदि यह एक फाइल है जहाँ आपको रूट अनुमतियों की आवश्यकता है:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

यह वास्तव में काम नहीं करता है जब लाइन एक के साथ शुरू होती है -
हाइपरनॉट 22

@zsero: अच्छी बात है! मैंने --grep कमांड में जोड़ा है, इसलिए यह किसी भी अधिक विकल्प के रूप में डैश के साथ शुरू होने वाली $ LINE की व्याख्या नहीं करेगा।
रूबो77

13

यहाँ एक sedसंस्करण है:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

यदि आपकी स्ट्रिंग एक चर में है:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

एक कमांड के लिए जो आंशिक स्ट्रिंग को पूर्ण / अपडेट की गई कॉन्फ़िगर फ़ाइल प्रविष्टि के साथ बदल देता है, या आवश्यकतानुसार लाइन जोड़ता है:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15

अच्छा लगा, इससे मुझे बहुत मदद मिली +1। क्या आप कृपया, अपनी sed अभिव्यक्ति की व्याख्या कर सकते हैं?
राहुल

मुझे इस समाधान का एक एनोटेट विवरण देखना अच्छा लगेगा। मैंने sedसालों से इस्तेमाल किया है लेकिन ज्यादातर सिर्फ sकमांड। holdऔर patternअंतरिक्ष स्वैप मुझे परे हैं।
अपराह्न

11

यदि एक संरक्षित फ़ाइल में लिखा है, तो @drAlberT और @ rubo77 के उत्तर आपके लिए काम नहीं कर सकते क्योंकि कोई सुडोल नहीं हो सकता >>। एक समान सरल उपाय , तब, tee --append(या MacOS पर, tee -a) का उपयोग करना होगा :

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

यदि, एक दिन, किसी और को इस कोड के साथ "विरासत कोड" के रूप में व्यवहार करना पड़ता है, तो वह व्यक्ति आभारी होगा यदि आप कम एक्सोटेरिक कोड लिखते हैं, जैसे कि

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
क्षमा करें, यदि आपको शेल नहीं पता है, तो जाकर Windows को व्यवस्थापन करें। && का उपयोग करके समाधान और || सामान्य शेल सिंटैक्स हैं, और किसी भी सक्षम व्यवस्थापक द्वारा समझा जाना चाहिए। वहां कुछ भी गूढ़ नहीं है।
ग्राहम निकोल्स

3
आपकी टिप्पणी ग्राहम के लिए धन्यवाद! हां, यह सामान्य शेल सिंटैक्स है। और हां, किसी भी सक्षम प्रशासन को इसे समझने की जरूरत है। हालांकि, यह शेल के भीतर अभिव्यक्ति मूल्यांकन एल्गोरिथ्म के एक साइड इफेक्ट पर आधारित है। तो आप इसे सीधे-सीधे छोड़कर, आप सभी को पसंद कर सकते हैं - जो कि मेरा मूल बिंदु था।
मार्सेलो वेंचुरा


6

एक अन्य सेड सॉल्यूशन है कि इसे हमेशा अंतिम पंक्ति में जोड़ें और पहले से मौजूद एक को हटा दें।

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

"ठीक से बच गए" का मतलब है कि आपकी प्रविष्टि से मेल खाने वाला रेगेक्स, अर्थात आपकी वास्तविक प्रविष्टि से सभी रेगेक्स नियंत्रणों से बचने के लिए, यानी ^ $ / *? + () के सामने बैकस्लैश डालना।

यह आपकी फ़ाइल की अंतिम पंक्ति पर विफल हो सकता है या यदि कोई झूलने वाली नई रेखा नहीं है, तो मुझे यकीन नहीं है, लेकिन यह कुछ निफ्टी शाखाओं द्वारा निपटा जा सकता है ...


1
अच्छा एक-लाइनर, छोटा और पढ़ने में आसान। आदि / मेजबानों को अद्यतन करने के लिए महान काम करता है:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos

एक छोटा संस्करण:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@ यदि मुझे लगता है कि यह विफल हो जाएगा, अगर प्रविष्टि पहले से ही फ़ाइल के अंत में है, क्योंकि dकमांड aसेड प्रोग्राम को पुनः आरंभ करेगा और इस तरह ppend को छोड़ देगा, अगर डिलीट अंतिम लाइन पर होना है।
रॉबिन 479

@ Robin479 डैम्ड, append से पहले निष्पादित किया जाना है dहटाएं: sed -i -e '$a<entry>' -e '/<entry>/d'। यह आपके मूल उत्तर से लगभग समान है।
जेराम पोइलर

3

awk का उपयोग करें

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

क्या यह 'फ़ाइल फ़ाइल' है या केवल 'फ़ाइल' है?
webdevguy

ऐसा file fileइसलिए है क्योंकि यह एक ही फाइल पर दो पास बनाता है। NR==FNRपहली पास पर सच है, लेकिन दूसरी नहीं। यह एक आम आवाम का मुहावरा है।
ट्रिपल

1

Grep का उपयोग करने वाले उत्तर गलत हैं। आपको पूरी पंक्ति से मेल करने के लिए एक -x विकल्प जोड़ने की आवश्यकता है अन्यथा लाइनें #text to addबिल्कुल तब भी मिलेंगी जब आप बिल्कुल जोड़ना चाहते हैंtext to add

तो सही समाधान कुछ इस तरह है:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

Sed का उपयोग करना: यह पंक्ति के अंत में सम्मिलित होगा। तुम भी हमेशा की तरह चर में पारित कर सकते हैं।

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

Oneliner sed का उपयोग करना

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

इको का उपयोग रूट के तहत काम नहीं कर सकता है, लेकिन इस तरह काम करेगा। लेकिन यह आपको चीजों को स्वचालित करने की अनुमति नहीं देगा यदि आप इसे करना चाहते हैं क्योंकि यह पासवर्ड मांग सकता है।

मुझे एक समस्या थी जब मैं किसी विशेष उपयोगकर्ता के लिए रूट से संपादित करने की कोशिश कर रहा था। बस $usernameपहले जोड़ना मेरे लिए एक फिक्स था।

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

Stackoverflow में आपका स्वागत है! कृपया उत्तर पोस्ट करने से पहले प्रश्न को ध्यान से पढ़ें - ओपी ने पूछा कि कैसे sed
Markoorn

-1

मुझे जरूरत पड़ने पर प्रतिबंधित लिखने की अनुमति वाली फ़ाइल को संपादित करने की आवश्यकता थी sudo। ghostdog74 के जवाब से काम करना और एक अस्थायी फ़ाइल का उपयोग करना:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

यह सही नहीं लगता है, यह फ़ाइल से अन्य लाइनों को खो देगा जब कॉन्फ़िगरेशन लाइन गायब है।
ट्रिपल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.