Bir kabuk komutunu sed ifadesine nasıl ekleyebilirim?


16

Aşağıdaki biçimde bir metin dosyası var:

keyword value
keyword value
...

Burada anahtar kelime tek bir kelimedir ve değer satır sonuna kadar her şeydir. Değerleri (ancak anahtar kelimeler değil) kabuk genişleme geçirecek şekilde, bir kabuk komut dosyasından dosyayı okumak istiyorum.

Sed ile anahtar kelimeleri ve değer parçalarını eşleştirmek kolaydır

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

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

hangi üretir

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

ama sonra soru sed komutunun yerine bir kabuk komutu nasıl ekleyebilirim. Bu durumda değiştirmenin olmasını istiyorum \1 `echo \2`.


Uhm ... Bir cevap olarak vereceğimden emin değilim, ancak sed ile alıntılanan DOUBLE komutunu ifade içinde shell $ (command) veya $ değişkenlerini kullanmalısınız.
St0rM

Yanıtlar:


18

Standart sed bir kabuk çağıramaz ( sadece gömülü olmayan Linux'u önemsiyorsanız GNU sed bunu yapmak için bir uzantıya sahiptir ), bu yüzden sed dışındaki işlemlerden bazılarını yapmanız gerekir. Birkaç çözüm var; hepsi dikkatli alıntılar gerektirir.

Değerlerin nasıl genişletilmesini istediğiniz açık değildir. Örneğin, bir çizgi

foo hello; echo $(true)  3

çıktı aşağıdakilerden hangisi olmalıdır?

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

Aşağıda çeşitli olasılıkları tartışacağım.

saf kabuk

Kabuğu giriş satır satır okuyabilir ve işleyebilirsiniz. Bu en basit çözüm ve aynı zamanda kısa dosyalar için en hızlı çözümdür. “ echo \2” Gereksiniminize en yakın şey bu :

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

read -r keyword valuekümeler $keywordilk boşluk ile ayrılmış hattın kelime ve $valuesatır eksi arka boşluk geri kalanına.

Değişken referanslarını genişletmek istiyor ancak komut yerine başka komutlar uygulamak istemiyorsanız, burada bir belgenin$value içine koyun . Gerçekten aradığın şey olduğundan şüpheleniyorum.

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

sed bir mermi içine borulu

Girişi kabuk betiğine dönüştürebilir ve değerlendirebilirsiniz. Sed o kadar da kolay değil. “ echo \2” Gereksiniminizi yerine getirmek (anahtar kelimedeki tek tırnak işaretlerinden kaçmamız gerektiğini unutmayın):

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

Burada bir belgeyle devam edersek, yine de anahtar kelimeden kaçmamız gerekir (ancak farklı).

{
  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

Çok fazla veriniz varsa bu en hızlı yöntemdir: her satır için ayrı bir işlem başlatmaz.

awk

Sed ile yaptığımız aynı teknikler awk ile çalışır. Ortaya çıkan program çok daha okunaklıdır. “ echo \2” İle devam etmek :

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

Burada bir belgeyi kullanma:

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

mükemmel cevap. giriş dosyası gerçekten küçük ve performans bir endişe değil, aynı zamanda temiz ve okunabilir olduğundan, saf kabuk çözüm kullanacağım.
Ernest AC

biraz kesmek ama yeterince temiz. Örneğin, uzun hex dizginin kodunu çözmek için xxd'yi çağırmak için sed kullanın. . . kedi FtH.ch13 | sed -r 's /(.* metin. *: [) ([0-9a-fA-F] *)] / \ 1 $ (echo \ 2 | xxd -r -p)] /; s / ^ ( . *) $ / echo "\ 1" / g '| bash> FtHtext.ch13 Burada FtH.ch13, "foo bar hex metin testi gibi satırlara sahiptir: [666f6f0a62617200]"
Ocak'ta

14

GNU'ya sahip olmak sediçin aşağıdaki komutu kullanabilirsiniz:

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

Hangi çıktılar:

keyword value value
keyword value  value
keyword Linux

giriş verilerinizle.

Açıklama:

Sed komutu, -nseçeneği kullanarak normal çıktıyı bastırır . -r, kalıptaki özel karakterlerden kaçmamızı sağlayan genişletilmiş düzenli ifadeler kullanmak için geçirilir, ancak gerekli değildir.

sKomut komutu içine giriş hattı transfer etmek için kullanılır:

echo "\1" \2

Get anahtar kelimesi değerin dışındadır. eGNU'ya özgü olan seçeneği s, sed'in ikame sonucunu bir kabuk komutu olarak yürütmesini ve sonuçların desen arabelleğine (hatta birden çok satır) okumasını söyleyen komuta iletiyorum. p(!) eÖğesinden sonra seçeneğinin kullanılması, sedkomut yürütüldükten sonra desen arabelleğinin yazdırılmasını sağlar.


Hem -nve pseçenekleri olmadan yapabilirsiniz sed -r 's/([^ ]+) (.*)/echo "\1" \2\n/e' input. Ama bunun için teşekkürler! eSeçenek hakkında bir şey bilmiyordum .
Kaushal Modi

@KaushalModi Oh evet, haklısın! eSeçenek (GNU tarafından tanıtıldı) söz konusu olduğunda çitin üzerinde oturuyorum . Hala bu sedmu? :)
hek2mgl

Benim için çalıştı. RHEL dağıtımında benim için varsayılan olarak GNU sed (GNU sed sürüm 4.2.1).
Kaushal Modi

4

Bu yaklaşımı deneyebilirsiniz:

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')"

(eğer niyetini doğru alırsam). Boş olmayan her satırın başlangıcına "script" eklemek için:

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

değerlendirilecek ( eval) burada süreç beklenen mesajı yazdıran bir işlevdir.


1

Sed olmayan bir çözüm kabul edilebilirse, bu PERL snippet'i işi yapacak:

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

1
teşekkürler ama ben eğer yapabilir ve standart unix komutları ve bourne kabuğu tutmak başka bir komut dosyası dili kullanmaktan kaçınmak istiyorum
Ernest AC

0

SADECE KISS KISA SAF SED

bunu yapacağım

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

ve işe yarıyor.


Nasıl çalıştığını açıklar mısınız?
elysch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.