次の形式のテキストファイルがあります。
keyword value
keyword value
...
ここで、keywordは単一のWordであり、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はシェルを呼び出せません( 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
行の最初の空白で区切られたWordに、そして$value
行の残りの部分から末尾の空白を引いたものに。
変数参照を拡張したいが、コマンド置換以外のコマンドを実行したくない場合は、$value
ここにドキュメント の内部。これはあなたが本当に探していたものだと思います。
while read -r keyword value; do
echo "k=<$keyword> v=<$(cat <<EOF
$value
EOF
)>"
done
入力をシェルスクリプトに変換して評価できます。 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
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
キーワードgetは値を引用していません。オプションe
-これはGNU固有です-s
コマンドに渡します。これは、置換の結果をシェルコマンドとして実行するようにsedに指示します。結果をパターンバッファに読み込む(複数行でも可)オプションp
after(!)e
を使用すると、コマンドの実行後にパターンバッファがsed
に出力されます。
あなたはこのアプローチを試すことができます:
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
)ここでprocessは、期待されるメッセージを出力する関数です。
Sed以外のソリューションが受け入れられる場合、このPerlスニペットが機能します。
$ echo "$input" | Perl -ne 'chomp; /^\s*(.+?)\s+(.+)$/ && do { $v=`echo "$2"`; chomp($v); print "k=<$1> v=<$v>\n"}'
ONLY KISS SHORT PURE SED
これをするつもりです
echo "ls_me" | sed -e "s/\(ls\)_me/\1/e" -e "s/to be/continued/g;"
そしてそれは働いています。