शेल कमांड को एक सेड एक्सप्रेशन में कैसे एम्बेड करें?


16

मेरे पास निम्नलिखित प्रारूप वाली एक पाठ फ़ाइल है:

keyword value
keyword value
...

जहाँ कीवर्ड एक शब्द है और लाइन के अंत तक वैल्यू बाकी सब कुछ है। मैं शेल स्क्रिप्ट से फ़ाइल को पढ़ना चाहता हूं, इस तरह से मान (लेकिन कीवर्ड नहीं) शेल विस्तार से गुजरते हैं।

Sed के साथ कीवर्ड और वैल्यू पार्ट्स को मैच करना आसान है

input='
keyword value value
keyword "value  value"
keyword `uname`
'

echo "$input"|sed -e 's/^\([^[:space:]]*\)[[:space:]]\(.*\)$/k=<\1> v=<\2>/'

जो पैदा करता है

k=<keyword> v=<value value>
k=<keyword> v=<"value  value">
k=<keyword> v=<`uname`>

लेकिन फिर सवाल यह है कि मैं एक शेल कमांड को sed अभिव्यक्ति के प्रतिस्थापन भाग में कैसे एम्बेड कर सकता हूं। इस मामले में मैं चाहूंगा कि प्रतिस्थापन हो \1 `echo \2`


उम्म ... मैं इसे उत्तर के रूप में देना निश्चित नहीं हूं, लेकिन sed के साथ उद्धृत DOUBLE का उपयोग करके आपको अभिव्यक्ति के अंदर शेल $ (कमांड) या $ चर का उपयोग करने देना चाहिए।
St0rM

जवाबों:


18

मानक सेड को शेल नहीं कहा जा सकता ( GNU sed में ऐसा करने के लिए एक एक्सटेंशन है , यदि आप केवल गैर-एम्बेडेड लिनक्स के बारे में परवाह करते हैं), तो आपको बाहर से कुछ प्रसंस्करण करना होगा। कई समाधान हैं; सभी को सावधान करने की आवश्यकता है।

यह बिल्कुल स्पष्ट नहीं है कि आप कैसे चाहते हैं कि मूल्यों का विस्तार हो। उदाहरण के लिए, यदि एक रेखा है

foo hello; echo $(true)  3

निम्नलिखित में से कौन सा आउटपुट होना चाहिए?

k=<foo> value=<hello; echo   3>
k=<foo> value=<hello; echo   3>
k=<foo> value=<hello; echo 3>
k=<foo> value=<foo hello
  3>

मैं नीचे कई संभावनाओं पर चर्चा करूंगा।

शुद्ध खोल

आप इनपुट लाइन को लाइन से पढ़ने और इसे प्रोसेस करने के लिए शेल प्राप्त कर सकते हैं। यह सबसे सरल समाधान है, और छोटी फ़ाइलों के लिए भी सबसे तेज़ है। यह आपकी आवश्यकता के लिए निकटतम चीज़ है " echo \2":

while read -r keyword value; do
  echo "k=<$keyword> v=<$(eval echo "$value")>"
done

read -r keyword valueसेट $keywordपंक्ति के पहले खाली स्थान के सीमांकित शब्द, और करने के लिए $valueलाइन शून्य से पिछली श्वेत रिक्ति के आराम करने के।

यदि आप परिवर्तनीय संदर्भों का विस्तार करना चाहते हैं, लेकिन कमांड प्रतिस्थापन के बाहर कमांड निष्पादित नहीं करते हैं, तो यहां$value एक दस्तावेज़ के अंदर रखें । मुझे संदेह है कि यह वही है जो आप वास्तव में देख रहे थे।

while read -r keyword value; do
  echo "k=<$keyword> v=<$(cat <<EOF
$value
EOF
)>"
done

एक खोल में सीना

आप इनपुट को शेल स्क्रिप्ट में बदल सकते हैं और उसका मूल्यांकन कर सकते हैं। सैड कार्य करने के लिए है, हालांकि यह इतना आसान नहीं है। अपनी " echo \2" आवश्यकता के साथ जा रहे हैं (ध्यान दें कि हमें कीवर्ड में एकल उद्धरण से बचने की आवश्यकता है):

sed  -e 's/^ *//' -e 'h' \
     -e 's/[^ ]*  *//' -e 'x' \
     -e 's/ .*//' -e "s/'/'\\\\''/g" -e "s/^/echo 'k=</" \
     -e 'G' -e "s/\n/>' v=\\</" -e 's/$/\\>/' | sh

यहां एक दस्तावेज़ के साथ जाने पर, हमें अभी भी कीवर्ड (लेकिन अलग-अलग) से बचना होगा।

{
  echo 'cat <<EOF'
  sed -e 's/^ */k=</' -e 'h' \
      -e 's/[^ ]*  *//' -e 'x' -e 's/ .*//' -e 's/[\$`]/\\&/g' \
      -e 'G' -e "s/\n/> v=</" -e 's/$/>/'
  echo 'EOF'
 } | sh

यदि आपके पास बहुत अधिक डेटा है तो यह सबसे तेज़ तरीका है: यह प्रत्येक पंक्ति के लिए एक अलग प्रक्रिया शुरू नहीं करता है।

awk

वही तकनीक जिनका इस्तेमाल हमने शामक के साथ सेड के साथ किया। परिणामी कार्यक्रम काफी पठनीय है। " echo \2" के साथ जा रहे हैं :

awk '
  1 {
      kw = $1;
      sub(/^ *[^ ]+ +/, "");
      gsub(/\047/, "\047\\\047\047", $1);
      print "echo \047k=<" kw ">\047 v=\\<" $0 "\\>";
  }' | sh

यहाँ एक दस्तावेज़ का उपयोग करना:

awk '
  NR==1 { print "cat <<EOF" }
  1 {
      kw = $1;
      sub(/^ *[^ ]+ +/, "");
      gsub(/\\\$`/, "\\&", $1);
      print "k=<" kw "> v=<" $0 ">";
  }
  END { print "EOF" }
' | sh

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

हैक का एक सा लेकिन काफी साफ है। उदाहरण के लिए लंबी हेक्स स्ट्रिंग को डीकोड करने के लिए xxd को कॉल करने के लिए sed का उपयोग करें। । । बिल्ली FtH.ch13 | sed -r 's /(.* टेक्स्ट। *: [) ([0-9a-fA-F] *)] / \ 1 $ (echo \ 2 | xxd -r -p)] /? s / ^ ( । *) $ / प्रतिध्वनि "\ 1" / g '| bash> FtHtext.ch13 जहाँ FtH.ch13 में "फू बार हेक्स पाठ परीक्षण जैसी पंक्तियाँ हैं: [666f6f0a62617200]
gaoithe

14

GNU होने से sedआप निम्न कमांड का उपयोग कर सकते हैं:

sed -nr 's/([^ ]+) (.*)/echo "\1" \2\n/ep' input

कौन से आउटपुट:

keyword value value
keyword value  value
keyword Linux

आपके इनपुट डेटा के साथ।

स्पष्टीकरण:

Sed कमांड -nविकल्प के उपयोग से नियमित आउटपुट को दबाती है। -rविस्तारित नियमित अभिव्यक्तियों का उपयोग करने के लिए पारित किया जाता है जो हमें पैटर्न में विशेष वर्णों के कुछ बचने से बचाता है लेकिन इसकी आवश्यकता नहीं है।

sआदेश आदेश में इनपुट लाइन हस्तांतरण करने के लिए प्रयोग किया जाता है:

echo "\1" \2

कीवर्ड ने मान को उद्धृत किया है। मैं विकल्प को पास करता हूं e- जो GNU विशिष्ट है - sकमांड के लिए, जो कि शेल कमांड के रूप में प्रतिस्थापन के परिणाम को निष्पादित करने के लिए sed को बताता है और पढ़ता है कि यह पैटर्न बफर (यहां तक ​​कि कई पंक्तियों) में परिणाम है। विकल्प का उपयोग pकरने के बाद (!) eबनाता sedपैटर्न बफर मुद्रण के बाद आदेश निष्पादित किया गया है।


आप विकल्प -nऔर pविकल्प दोनों के बिना कर सकते हैं sed -r 's/([^ ]+) (.*)/echo "\1" \2\n/e' input। लेकिन इसके लिए धन्यवाद! मुझे eविकल्प के बारे में नहीं पता था ।
कौशल मोदी

@ कौशलमोडी ओह, हाँ, आप सही हैं! जब यह eविकल्प (GNU द्वारा प्रस्तुत) की बात आती है तो मैं धरने पर बैठा हूँ । क्या वह अभी भी है sed? :)
hek2mgl

खैर, यह मेरे लिए काम किया। यह आरएचईएल वितरण पर मेरे लिए जीएनयू sed डिफ़ॉल्ट रूप से (GNU sed संस्करण 4.2.1) है।
कौशल मोदी

4

आप इस दृष्टिकोण की कोशिश कर सकते हैं:

input='
keyword value value
keyword "value  value"
keyword `uname`
'

process() {
  k=$1; shift; v="$*"
  printf '%s\n' "k=<$k> v=<$v>"
}

eval "$(printf '%s\n' "$input" | sed -n 's/./process &/p')"

(अगर मुझे आपका इरादा सही लगता है)। इसे बनाने के लिए प्रत्येक गैर-रिक्त लाइन की शुरुआत में "प्रक्रिया" सम्मिलित करें जैसे:

process keyword value value
process keyword "value  value"
process keyword `uname`

मूल्यांकन किया जाना है ( eval) जहां प्रक्रिया एक फ़ंक्शन है जो अपेक्षित संदेश प्रिंट करता है।


1

यदि कोई गैर-सेड समाधान स्वीकार्य है, तो यह पेरल स्निपेट काम करेगा:

$ echo "$input" | perl -ne 'chomp; /^\s*(.+?)\s+(.+)$/ && do { $v=`echo "$2"`; chomp($v); print "k=<$1> v=<$v>\n"}'

1
धन्यवाद, लेकिन मैं एक और पटकथा भाषा का उपयोग करने से बचूंगा अगर मैं इसे कर सकता हूं और इसे मानक यूनिक्स कमांड और बोर्न शेल में रख सकता हूं
अर्नेस्ट एसी

0

केवल छोटे शुद्ध चुंबन एसईडी

यह मैं करूँगा

echo "ls_me" | sed -e "s/\(ls\)_me/\1/e" -e "s/to be/continued/g;"

और यह काम कर रहा है।


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