sed式にシェルコマンドを含める方法は?

sed式にシェルコマンドを含める方法は?

次の形式のテキストファイルがあります。

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`

答え1

GNU では、sed次のコマンドを使用できます。

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

任意の出力:

keyword value value
keyword value  value
keyword Linux

あなたの入力データとして。

説明する:

-n通常の出力を抑制するには、sedコマンドでこのオプションを使用します。-rパターンで特殊文字の一部のエスケープを防ぐ拡張正規表現を使用するには、渡しますが必須ではありません。

このsコマンドは、入力行をコマンドに変換するために使用されます。

echo "\1" \2

キーワードは引用され、値は引用されません。 sedに代替コマンドをシェルコマンドとして実行し、その結果をパターンバッファ(複数行を含む)に読み込むように指示するコマンドにeオプション(GNU関連)を渡します。コマンドの実行後にパターンバッファを印刷するには、after(!)オプションをs使用します。pesed

答え2

標準sedはシェルを呼び出すことはできません(これを行うためのGNU sed拡張があります。(組み込みLinuxにのみ興味がある場合)、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>

以下でいくつかの可能性について議論します。

純粋な殻

シェルに入力を1行ずつ読み込み、処理させることができます。これは最も簡単なソリューションであり、短いファイルの場合は最速のソリューションです。これはお客様の要件" 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

sedがシェルにパイプされる

入力をシェルスクリプトに変換して評価できます。 Sedは簡単ではありませんが、作業を行います。 " "要件を満たす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

データが多い場合、これは最速の方法です。行ごとに別々のプロセスを開始しません。

アッ

sed で使用する技術は 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

答え3

次の方法を試すことができます。

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」を挿入して、次のスクリプトにします。

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

評価対象(evalプロセス期待されるメッセージを印刷する関数です。

答え4

ONLY KISS ショートピュア SED

やるよ

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

そしてそれはうまくいきます。

関連情報