एक टैब के रूप में sed पहचान क्यों नहीं रहा है?


105
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename

मैं इस sedलिपि की अपेक्षा कर रहा हूं tabकि हर पंक्ति के सामने एक इंसर्ट डाला जाए $filenameलेकिन ऐसा नहीं है। किसी कारण के लिए यह एक सम्मिलित है t


1
जैसा कि प्लेटफॉर्म (विशेष रूप से, बीएसडी / मैकओएसएक्स बनाम लिनक्स) के बीच सेड भिन्न हो सकता है, यह उस प्लेटफॉर्म को निर्दिष्ट करने में मददगार हो सकता है, जिस पर आप सेड का उपयोग कर रहे हैं।
इसहाक

sed "s / (*। *) / # \ 1 /" $ फ़ाइल नाम | tr '#' '\ t'> $ sedTmpFile && mv $ sedTmpFile $ फ़ाइल नाम।
user2432405

OS X (macOS) उपयोगकर्ता के लिए, इस प्रश्न का संदर्भ लें ।
फ्रैंकलिन यू

जवाबों:


129

sedसमझ के सभी संस्करण नहीं \t। इसके बजाय बस एक शाब्दिक टैब डालें (दबाएं Ctrl- Vतब Tab)।


2
आह येस; स्पष्ट करने के लिए: sed के सभी संस्करणों \tको अभिव्यक्ति के प्रतिस्थापन भाग में समझ में नहीं आता है (यह \tपैटर्न मिलान भाग में ठीक पहचाना गया है)
जॉन वेल्डन

3
awwwwwwwwwwwwwwwww, ठीक है कि बहुत दिलचस्प है। और अजीब है। आप इसे एक स्थान पर क्यों पहचानेंगे लेकिन दूसरे को नहीं ...?
साठफुटेरसूड 19

2
एक स्क्रिप्ट से कॉल किया जाता है, जो काम नहीं करेगा: टैब को sh द्वारा अनदेखा किया जाएगा। उदाहरण के लिए, एक शेल स्क्रिप्ट से निम्न कोड $ TEXT_TO_ADD जोड़ देगा, इसे सारणीयन द्वारा प्रस्तुत किए बिना: sed "$ {LINE} a \\ $ TEXT_TO_ADD" $ FILE।
डेरेकसन

2
@ डेरेकसन और अन्य - इसका उत्तर देखें: stackoverflow.com/a/2623007/48082
चीज़ो

2
डेरेकसन एस / कर सकते हैं / नहीं /?
डगलस हेल्ड

41

बैश का उपयोग करके आप एक TAB वर्ण प्रोग्राम की तरह सम्मिलित कर सकते हैं:

TAB=$'\t' 
echo 'line' | sed "s/.*/${TAB}&/g" 
echo 'line' | sed 's/.*/'"${TAB}"'&/g'   # use of Bash string concatenation

यह बहुत मददगार है।
चेसो

1
आप सही रास्ते पर थे, $'string'लेकिन अभाव की व्याख्या के साथ। वास्तव में मुझे संदेह है, क्योंकि बहुत ही अजीबोगरीब उपयोग के कारण जो आपको शायद एक अधूरी समझ है (जैसा कि हम में से ज्यादातर लोग मार खाते हैं)। नीचे मेरी व्याख्या देखें: stackoverflow.com/a/43190120/117471
ब्रूनो ब्रोंस्की

1
याद रखें कि BASH $TABसिंगल कोट्स के अंदर वैरिएबल का विस्तार नहीं करेगा , इसलिए आपको इसे डबल कोट्स का उपयोग करने की आवश्यकता होगी।
nealmcb

*दोहरे उद्धरण चिह्नों का उपयोग करने के बारे में सावधान ... यह एक ग्लोब के रूप में माना जाएगा, न कि आपके द्वारा बताए गए रीगेक्स के रूप में।
लेविग्रोकर

28

@ हेरिटेज सही रास्ते पर था, लेकिन एक चर को परिभाषित करना थोड़ा अजीब है।

समाधान (बैश विशिष्ट)

बैश में ऐसा करने का तरीका आपके एकल उद्धृत स्ट्रिंग के सामने एक डॉलर का चिह्न रखना है।

$ echo -e '1\n2\n3'
1
2
3

$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3

$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
    1
    2
    3

यदि आपके स्ट्रिंग में परिवर्तनशील विस्तार को शामिल करने की आवश्यकता है, तो आप उद्धृत स्ट्रिंग्स को एक साथ रख सकते हैं:

$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958  1
1491237958  2
1491237958  3

व्याख्या

बैश में $'string'"एएनएसआई-सी विस्तार" का कारण बनता है। और यही हम में से ज्यादातर की उम्मीद है जब हम जैसी चीजों का उपयोग है \t, \r, \n: आदि, से https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

फॉर्म $ 'स्ट्रिंग' के शब्दों को विशेष रूप से व्यवहार किया जाता है। यह शब्द ANSI C मानक द्वारा निर्दिष्ट बैकस्लैश-एस्कैप्ड वर्णों के साथ स्ट्रिंग में फैलता है । बैकस्लैश एस्केप सीक्वेंस, अगर मौजूद हैं, तो डिकोड हो गए हैं ...

विस्तारित परिणाम एकल-उद्धृत है, जैसे कि डॉलर का चिह्न मौजूद नहीं था।

समाधान (यदि आपको बैश से बचना चाहिए)

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

$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
    1
    2
    3

* सबसे बढ़िया उत्तर? हाँ, क्योंकि ज्यादातर विरोधी पार्टी खोल scripters उनके कोड में गलत क्या करना होगा का एक उदाहरण इस्तेमाल होता है echo '\t'के रूप में @ robrecord का जवाब । यह GNU इको के लिए काम करेगा, लेकिन BSD इको के लिए नहीं। इसे http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 पर द ओपन ग्रुप द्वारा समझाया गया है और यह इस बात का उदाहरण है कि क्यों बशर्ते से बचने की कोशिश असफल हो रही है।


8

मैंने Ubuntu 12.04 (LTS) पर बैश शेल के साथ कुछ इस तरह का उपयोग किया है:

टैब के साथ एक नई लाइन जोड़ने के लिए , दूसरा जब पहली बार मिलान किया जाता है:

sed -i '/first/a \\t second' filename

टैब के साथ पहले को बदलने के लिए , दूसरा :

sed -i 's/first/\\t second/g' filename

4
डबल एस्केप कुंजी है, अर्थात उपयोग \\tऔर नहीं \t
ज़मनेट्स

मुझे Ubuntu 16.04 और Bash 4.3 पर एकल उद्धरणों के बजाय दोहरे उद्धरण चिह्नों का उपयोग करना पड़ा।
कांव-कांव

4

का उपयोग करें $(echo '\t')। आपको पैटर्न के आसपास उद्धरणों की आवश्यकता होगी।

उदाहरण के लिए। टैब निकालने के लिए:

sed "s/$(echo '\t')//"

5
यह मजेदार है कि आप "BSD sed" विशिष्ट बग (2 अलग-अलग वर्णों के रूप में \ t की व्याख्या कर रहे हैं) को हल करने के लिए "GNU इको" विशिष्ट सुविधा (टैब वर्ण के रूप में व्याख्या करना) का उपयोग कर रहे हैं। संभवतः, यदि आपके पास "GNU इको" है, तो आपके पास "GNU sed" भी होगा। जिस स्थिति में आपको इको का उपयोग करने की आवश्यकता नहीं होगी। बीएसडी गूंज के साथ echo '\t'2 अलग-अलग पात्रों का उत्पादन करने जा रहा है। POSIX पोर्टेबल तरीका उपयोग करना है printf '\t'। यही कारण है कि मैं कहता हूं: बैश का उपयोग न करके अपने कोड को पोर्टेबल बनाने की कोशिश न करें। आपके विचार से यह कठिन है। उपयोग करना bashसबसे पोर्टेबल चीज़ है जो हम में से अधिकांश कर सकते हैं।
ब्रूनो ब्रोंस्की

3

sedवास्तविक स्थिति में होने पर आपको प्रतिस्थापन करने के लिए उपयोग करने की आवश्यकता नहीं होती है, आप केवल पंक्ति के सामने एक टैब सम्मिलित करना चाहते हैं। इस मामले के लिए प्रतिस्थापन एक महंगा ऑपरेशन है क्योंकि इसे केवल प्रिंट करने की तुलना में, खासकर जब आप बड़ी फ़ाइलों के साथ काम कर रहे हैं। यह पढ़ने में आसान है, क्योंकि यह रेगेक्स नहीं है।

जैसे कि awk का उपयोग करना

awk '{print "\t"$0}' $filename > temp && mv temp $filename


0

sedसमर्थन नहीं करता है \t, और न ही \nउस मामले के लिए अन्य एस्केप सीक्वेंस । इसका एकमात्र तरीका मुझे यह करना है कि वास्तव में स्क्रिप्ट का उपयोग करके टैब वर्ण सम्मिलित करना था sed

उस ने कहा, आप पर्ल या अजगर का उपयोग करने पर विचार कर सकते हैं। यहाँ एक छोटी पायथन लिपि है जो मैंने लिखी है कि मैं सभी स्ट्रीम रेगेक्सिंग के लिए उपयोग करता हूँ:

#!/usr/bin/env python
import sys
import re

def main(args):
  if len(args) < 2:
    print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
    raise SystemExit

  p = re.compile(args[0], re.MULTILINE | re.DOTALL)
  s = sys.stdin.read()
  print p.sub(args[1], s),

if __name__ == '__main__':
  main(sys.argv[1:])

2
और पर्ल संस्करण शेल वन-लाइनर "perl -pe / s / a / b / 'filename" या "कुछ" होगा। perl -pe / s / a / b /' "
tiftik


0

मुझे लगता है कि दूसरों के अन्य तरीकों (के लिए पर्याप्त रूप से इस स्पष्ट किया है sed, AWK, आदि)। हालाँकि, मेरे bashविशिष्ट जवाब (macOS उच्च सिएरा और CentOS 6/7 पर परीक्षण) का पालन करें।

1) अगर ओपी मूल रूप से प्रस्तावित की तरह एक खोज-और-प्रतिस्थापित विधि का उपयोग करना चाहता था, तो मैं इसके perlलिए उपयोग करने का सुझाव दूंगा , इस प्रकार। नोट: रेगेक्स के लिए कोष्ठक से पहले बैकस्लैश आवश्यक नहीं होना चाहिए, और यह कोड लाइन दर्शाती है कि प्रतिस्थापन ऑपरेटर (जैसे प्रति पर्ल 5 प्रलेखन ) के साथ $1उपयोग करना बेहतर है ।\1perl

perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename

2) हालांकि, जैसा कि ghostdog74 द्वारा बताया गया है , चूंकि वांछित ऑपरेशन वास्तव में इनपुट / लक्ष्य फ़ाइल ( ) में tmp फ़ाइल को बदलने से पहले प्रत्येक पंक्ति की शुरुआत में एक टैब जोड़ने के लिए है $filename, मैं perlफिर से सिफारिश करूंगा लेकिन निम्नलिखित संशोधन के साथ (ओं):

perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename

3) बेशक, tmp फ़ाइल है ज़रूरत से ज़्यादा है, तो यह सिर्फ 'जगह में' सब कुछ करना (जोड़ने के लिए बेहतर है -iके साथ एक और अधिक सुरुचिपूर्ण एक लाइनर करने के लिए ध्वज) और सरल बातें

perl -i -pe $'s/^/\t/' $filename
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.