मैं कमांड में किसी फ़ाइल का उपयोग कैसे कर सकता हूं और उसी फाइल को बिना ट्रंक किए हुए आउटपुट पर रीडायरेक्ट कर सकता हूं?


96

मूल रूप से मैं एक फाइल से इनपुट टेक्स्ट के रूप में लेना चाहता हूं, उस फाइल से एक लाइन हटाता हूं, और आउटपुट को उसी फाइल पर वापस भेजता हूं। इन पंक्तियों के साथ कुछ अगर यह किसी भी स्पष्ट बनाता है।

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name

हालाँकि, जब मैं ऐसा करता हूं तो मैं एक रिक्त फ़ाइल के साथ समाप्त होता हूं। कोई विचार?


जवाबों:


84

आप ऐसा नहीं कर सकते क्योंकि बैश पहले पुनर्निर्देशन की प्रक्रिया करता है, फिर कमांड निष्पादित करता है। इसलिए जब तक grep file_name को देखता है, वह पहले से ही खाली है। यद्यपि आप एक अस्थायी फ़ाइल का उपयोग कर सकते हैं।

#!/bin/sh
tmpfile=$(mktemp)
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
cat ${tmpfile} > file_name
rm -f ${tmpfile}

उस तरह, tmpfilemktemp बनाने के लिए उपयोग करने पर विचार करें लेकिन ध्यान दें कि यह POSIX नहीं है।


47
आप ऐसा क्यों नहीं कर सकते इसका कारण यह है: बैश पहले पुनर्निर्देशन को संसाधित करता है, फिर कमांड निष्पादित करता है। इसलिए जब तक grep file_name को देखता है, वह पहले से ही खाली है।
ग्लेन जैकमैन

1
@glennjackman: "प्रोसेस पुनर्निर्देशन से आपका मतलब है कि> के मामले में यह फ़ाइल को खोलता है और इसे साफ़ करता है और >> के मामले में यह केवल इसे खोलता है"?
रजवन

2
हां, लेकिन इस स्थिति में ध्यान दें, >पुनर्निर्देशन फ़ाइल को खोल देगा और शेल के लॉन्च से पहले इसे काट देगा grep
ग्लेन जैकमैन

1
यदि आप एक अस्थायी फ़ाइल का उपयोग नहीं करना चाहते हैं, तो मेरा उत्तर देखें , लेकिन कृपया इस टिप्पणी को अपवित्र न करें।
ज़ैक मॉरिस

इसके बजाय, कमांड का उपयोग करनेsponge वाले उत्तर को स्वीकार किया जाना चाहिए।
vlz

95

इस तरह के कार्यों के लिए स्पंज का उपयोग करें । इसका अधिक हिस्सा है।

इस आदेश का प्रयास करें:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name

4
जवाब के लिए धन्यवाद। संभावित रूप से उपयोगी जोड़ के रूप में, यदि आप मैक पर होमब्रे का उपयोग कर रहे हैं, तो उपयोग कर सकते हैं brew install moreutils
एंथोनी पनोजो

2
या sudo apt-get install moreutilsडेबियन-आधारित प्रणालियों पर।
जोनाह

3
अरे नहीं! मुझे और अधिक जानकारी देने के लिए धन्यवाद =) वहाँ कुछ अच्छे कार्यक्रम!
netigger

बचाव के लिए बहुत बहुत धन्यवाद! बॉस की तरह स्पंज!
aqquadro

3
सावधानी का शब्द, "स्पंज" विनाशकारी है, इसलिए यदि आपके आदेश में कोई त्रुटि है, तो आप अपनी इनपुट फ़ाइल मिटा सकते हैं (जैसा कि मैंने पहली बार स्पंज की कोशिश कर रहा था)। सुनिश्चित करें कि आपका कमांड काम करता है, और / या इनपुट फ़ाइल संस्करण नियंत्रण में है यदि आप कमांड को काम करने पर पुनरावृत्त करने की कोशिश कर रहे हैं।
user107172

18

इसके बजाय sed का उपयोग करें:

sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name

1
आईआईआरसी -iजीएनयू केवल विस्तार है, सिर्फ नोटिंग।
c00kiemon5ter

3
* बीएसडी (और इसलिए ओएसएक्स) पर भी आप कह सकते हैं -i ''कि विस्तार सख्ती से अनिवार्य नहीं है, लेकिन -iविकल्प में कुछ तर्क की आवश्यकता होती है।
ट्रिपल

13

इस सरल एक कोशिश करो

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name

आपकी फ़ाइल इस बार खाली नहीं होगी :) और आपका आउटपुट भी आपके टर्मिनल पर प्रिंट हो जाएगा।


1
मुझे यह समाधान पसंद है! और अगर आप नहीं चाहते हैं कि इसे टर्मिनल में प्रिंट किया जाए तो आप अभी भी आउटपुट को /dev/nullया इसी तरह के स्थानों पर भेज सकते हैं।
फ्रोज़न

4
यह फ़ाइल सामग्री को यहाँ भी साफ़ करता है। क्या यह GNU / BSD अंतर के कारण है? मैं macOS पर हूँ ...
11:24 पर

7

आप एक ही फ़ाइल पर पुनर्निर्देशन ऑपरेटर ( >या >>) का उपयोग नहीं कर सकते हैं , क्योंकि इसकी एक उच्च मिसाल है और यह कमांड के लागू होने से पहले फ़ाइल को क्रुणित / निर्मित कर देगा। कि बचने के लिए आप इस तरह के रूप में उपयुक्त उपकरणों का उपयोग करना चाहिए tee, sponge, sed -iया किसी अन्य उपकरण फ़ाइल के लिए परिणाम लिख सकते हैं जो (जैसेsort file -o file )।

मूल रूप से एक ही मूल फ़ाइल पर इनपुट पुनर्निर्देशित करने का कोई मतलब नहीं है और आपको इसके लिए उपयुक्त इन-एडिटर्स का उपयोग करना चाहिए, उदाहरण के लिए Ex editor (Vim का हिस्सा):

ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name

कहाँ पे:

  • '+cmd'/-c - कोई भी Ex / Vim कमांड चलाएं
  • g/pattern/d- वैश्विक का उपयोग कर एक पैटर्न से मेल खाती लाइनें निकालें (help :g )
  • -s- मूक मोड ( man ex)
  • -c wq- निष्पादित :writeऔर :quitआदेश

आप उपयोग कर सकते हैं sed, एक ही प्राप्त करने के लिए (पहले से ही अन्य उत्तर में दिखाया गया है) फिर भी यथा-स्थान ( -i) (यूनिक्स / लिनक्स के बीच अलग तरह से काम कर सकते हैं) अमानक FreeBSD विस्तार है और मूल रूप से यह एक है रों tream एड itor, न कि एक फ़ाइल संपादक । देखें: क्या Ex मोड का कोई व्यावहारिक उपयोग है?


6

एक लाइनर विकल्प - फ़ाइल की सामग्री को चर के रूप में सेट करें:

VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name

4

चूँकि यह प्रश्न खोज इंजनों में शीर्ष परिणाम है, यहाँ https://serverfault.com/a/547331 पर आधारित एक-लाइनर है spongeजो इसके बजाय एक उप- भाग का उपयोग करता है (जो अक्सर OS X जैसे वेनिला इंस्टॉल का हिस्सा नहीं होता है) :

echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name

सामान्य मामला है:

echo "$(cat file_name)" > file_name

संपादित करें, उपरोक्त समाधान में कुछ चेतावनी हैं:

  • printf '%s' <string>इसके बजाय इसका उपयोग किया जाना चाहिए echo <string>ताकि फ़ाइलों में -nअवांछित व्यवहार न हो।
  • कमान प्रतिस्थापन स्ट्रिप्स नई-पंक्तियों अनुगामी ( इस बैश की तरह गोले एक बग / सुविधा है ) तो हम की तरह एक पोस्टफ़िक्स चरित्र संलग्न करना होगा xउत्पादन के लिए और के माध्यम से बाहर की दुनिया में इसे हटाने के लिए एक अस्थायी चर के पैरामीटर विस्तार की तरह${v%x}
  • अस्थायी चर का उपयोग $vकरने $vसे मौजूदा शेल वातावरण में किसी भी मौजूदा चर का मूल्य समाप्त हो जाता है, इसलिए हमें पिछले मूल्य को संरक्षित करने के लिए कोष्ठक में संपूर्ण अभिव्यक्ति को घोंसला बनाना चाहिए।
  • बैश जैसे गोले की एक और बग / विशेषता यह है कि nullआउटपुट से कमांड कमांड प्रतिस्थापन स्ट्रिप्स अनपेक्षित वर्णों की तरह है। मैंने dd if=/dev/zero bs=1 count=1 >> file_nameइसे हेक्स में कॉल करके और इसे देखकर सत्यापित किया है cat file_name | xxd -p। लेकिन echo $(cat file_name) | xxd -pछीन लिया जाता है। तो इस जवाब का उपयोग बाइनरी फ़ाइलों या अनपेक्षित वर्णों का उपयोग करके कुछ भी नहीं किया जाना चाहिए , जैसा कि लिंच ने कहा है

सामान्य समाधान (अल्बाइट थोड़ा धीमा, अधिक स्मृति गहन और अभी भी अप्राप्य अक्षर अलग करना) है:

(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)

Https://askubuntu.com/a/752451 से टेस्ट :

printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do (v=$(cat file_uniquely_named.txt; printf x); printf '%s' ${v%x} > file_uniquely_named.txt); done; cat file_uniquely_named.txt; rm file_uniquely_named.txt

प्रिंट करना चाहिए:

hello
world

जबकि cat file_uniquely_named.txt > file_uniquely_named.txtवर्तमान शेल में कॉलिंग :

printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt

एक खाली स्ट्रिंग प्रिंट करता है।

मैंने इसे बड़ी फ़ाइलों पर परीक्षण नहीं किया है (शायद 2 या 4 जीबी से अधिक)।

मैंने यह जवाब हार्ट सिम्हा और कोस से लिया है ।


2
बेशक यह बड़ी फ़ाइल के साथ काम नहीं करेगा। यह संभवतः एक अच्छा समाधान नहीं हो सकता है या हर समय काम कर सकता है। क्या हो रहा है कि बैश पहले कमांड निष्पादित करें और फिर स्टैडआउट लोड करें catऔर इसे पहले तर्क के रूप में डालें echo। बेशक गैर मुद्रण योग्य चर ठीक से आउटपुट नहीं करेंगे और डेटा को दूषित करेंगे। किसी फ़ाइल को स्वयं पर पुनर्निर्देशित करने का प्रयास न करें, यह सिर्फ अच्छा नहीं हो सकता।
लिंच

1

वहाँ भी है ed(एक विकल्प के रूप में sed -i):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq |  ed -s file_name

1

आप प्रक्रिया-प्रतिस्थापन का उपयोग कर सकते हैं ।

यह एक हैक का एक सा है, हालांकि बैश के रूप में अतुल्य रूप से सभी पाइपों को खोलता है और हमें sleepYMMV का उपयोग करके चारों ओर काम करना होगा ।

आपके उदाहरण में:

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
  • >(sleep 1 && cat > file_name) एक अस्थायी फ़ाइल बनाता है जो grep से आउटपुट प्राप्त करता है
  • sleep 1 इनपुट फ़ाइल को पार्स करने के लिए grep समय देने के लिए एक सेकंड के लिए देरी
  • आखिर cat > file_nameमें आउटपुट लिखता है

1

आप POSIX Awk के साथ घोल का उपयोग कर सकते हैं:

!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
  q = q ? q RS $0 : $0
}
END {
  print q > ARGV[1]
}

उदाहरण


1
यह शायद बताया जाना चाहिए कि "स्लरप" का अर्थ है "पूरी फ़ाइल को मेमोरी में पढ़ें"। यदि आपके पास एक बड़ी इनपुट फ़ाइल है, तो शायद आप उससे बचना चाहते हैं।
ट्रिपल

1

यह बहुत संभव है, आपको बस यह सुनिश्चित करना है कि जब तक आप आउटपुट लिखते हैं, तब तक आप इसे एक अलग फाइल पर लिख रहे हैं। यह फ़ाइल डिस्क्रिप्टर को खोलने के बाद फ़ाइल को हटाकर किया जा सकता है, लेकिन इसे लिखने से पहले:

exec 3<file ; rm file; COMMAND <&3 >file ;  exec 3>&-

या लाइन से लाइन, इसे बेहतर समझने के लिए:

exec 3<file       # open a file descriptor reading 'file'
rm file           # remove file (but fd3 will still point to the removed file)
COMMAND <&3 >file # run command, with the removed file as input
exec 3>&-         # close the file descriptor

यह अभी भी एक जोखिम भरा काम है, क्योंकि अगर COMMAND ठीक से नहीं चल पाता है, तो आप फ़ाइल सामग्री खो देंगे। यदि COMMAND एक गैर-शून्य निकास कोड लौटाता है, तो फ़ाइल को पुनर्स्थापित करके इसे कम किया जा सकता है:

exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-

हम इसे उपयोग करने में आसान बनाने के लिए शेल फ़ंक्शन को भी परिभाषित कर सकते हैं:

# Usage: replace FILE COMMAND
replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }

उदाहरण :

$ echo aaa > test
$ replace test tr a b
$ cat test
bbb

इसके अलावा, ध्यान दें कि यह मूल फ़ाइल की पूरी प्रतिलिपि रखेगा (जब तक कि तीसरा फ़ाइल विवरणक बंद न हो)। यदि आप लिनक्स का उपयोग कर रहे हैं, और जिस फाइल पर आप प्रोसेसिंग कर रहे हैं, वह डिस्क पर दो बार फिट होने के लिए बहुत बड़ी है, तो आप इस स्क्रिप्ट की जांच कर सकते हैं, जो पहले से ही प्रोसेस को अनलॉकेट करते हुए निर्दिष्ट कमांड ब्लॉक-बाय-ब्लॉक में फाइल को पाइप कर देगा। ब्लॉक। हमेशा की तरह, उपयोग पृष्ठ में चेतावनी पढ़ें।


0

इसे इस्तेमाल करे

echo -e "AAA\nBBB\nCCC" > testfile

cat testfile
AAA
BBB
CCC

echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC

एक छोटी व्याख्या या यहां तक ​​कि टिप्पणियाँ सहायक हो सकती हैं।
रिच

मुझे लगता है, यह काम करता है क्योंकि स्ट्रिंग एक्सट्रपलेशन रीडायरेक्ट ऑपरेटर से पहले निष्पादित होता है, लेकिन मुझे ठीक से पता नहीं है
Виктор Пупкин

0

निम्नलिखित एक ही चीज़ को पूरा करेगा जो spongeआवश्यकता के बिना करता है moreutils:

    shuf --output=file --random-source=/dev/zero 

--random-source=/dev/zeroभाग चालshuf बिना किसी फेरबदल के अपना काम करने की कोशिश करता है, इसलिए यह आपके इनपुट को बिना बदले बदल देगा।

हालांकि, यह सच है कि प्रदर्शन कारणों से एक अस्थायी फ़ाइल का उपयोग करना सबसे अच्छा है। तो, यहाँ एक फ़ंक्शन है जो मैंने लिखा है जो आपके लिए एक सामान्यीकृत तरीके से करेगा:

# Pipes a file into a command, and pipes the output of that command
# back into the same file, ensuring that the file is not truncated.
# Parameters:
#    $1: the file.
#    $2: the command. (With $3... being its arguments.)
# See https://stackoverflow.com/a/55655338/773113

function siphon
{
    local tmp=$(mktemp)
    local file="$1"
    shift
    $* < "$file" > "$tmp"
    mv "$tmp" "$file"
}

-2

मैं आमतौर पर ऐसा करने के लिए टी कार्यक्रम का उपयोग करता हूं :

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name

यह अपने आप में एक टेंपरेचर बनाता और हटाता है।


क्षमा करें, teeकाम करने की गारंटी नहीं है। Askubuntu.com/a/752451/335781 देखें ।
स्टडजेक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.