web-dev-qa-db-ja.com

xargs sh –c内の他のコマンドと組み合わせてawkを使用するための正しい構文

このコマンドを機能させる方法:

ls * | xargs -I {} sh -c 'echo {}; awk '{print $1}' {} | uniq'

それは単純なことをしなければなりません:フォルダ内の各ファイルの名前と最初の列のuniq値を出力します

$記号は文字列記号の終わりとして認識され、引用符で処理する必要があると思います。

エラーメッセージ:

awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string
3
Ekaterina

2番目の単一引用符は、最初の単一引用符付き文字列'echo {}; awk 'を終了します。次に、{print $1}は引用符で囲まれず、単一引用符で囲まれた別の文字列' {} | uniq'があります。これは、構文が強調表示されているエディタでは明確であるはずです。また、質問で構文の強調表示を見ると明らかです。

ここで最も簡単なアプローチは、ネストされた引用を完全に避けることです。 awkスクリプトを引数としてshに渡します。

xargs -I {} sh -c 'echo "$1"; awk "$0"' '{print $1}' {} | uniq'

(また、スクリプト内の{}shの対応する引数に置き換えました。スクリプト内で{}を使用しないでください。ファイル名としてではなく、シェル構文として解析されます。そのため、シェルの特殊文字を含むファイル名では致命的に失敗します。)

単一引用符で囲まれたリテラルに単一引用符を効果的に含めるには、'\''を使用します(これにより、単一引用符で囲まれたリテラルが終了し、前のバックフラッシュのために文字どおりに解釈される単一引用符が追加され、次に別の単一引用符で囲まれますリテラルですが、効果は同じです)。

xargs -I {} sh -c 'echo {}; awk '\''{print $1}'\'' {} | uniq'

または、一方のレベルで一重引用符を使用し、もう一方のレベルで二重引用符を使用しますが、扱いが難しくなります。

ls *などの無意味なコマンドは、非常に単純化された例にすぎません。)

xargsはまったく必要ありません。

私がこのサイトの別の場所で読んだように(申し訳ありませんが、どこかだけでは思い出せません)、トップユーザーから:

はい、xargsはかっこいいおもちゃです。いいえ、使用する必要はありません。

この:

ls * | xargs -I {} sh -c 'echo {}; awk '{print $1}' {} | uniq'

完全に置き換えにすることができます:

for f in *; do echo "$f"; awk '{print $1}' "$f" | uniq; done

これにより、可読性や実際の機能は言うまでもなく、以前のバージョンよりも大幅にセキュリティ向上します。 (もちろん、最初のバージョンは一重引用符をネストしようとしたため不可能です **​​ 。)

ただし、自分のバージョンの引用を修正しても、あなたは自分自身を大きく広げています。 -c内のシェルコマンドに任意のファイル名の名前を詰め込むことにより、そのファイル名でeval効果的に実行し、- 特定のファイル名を作成することにより、実行可能なエクスプロイトが多数あります 。たとえば、touch ';rm -rf "$HOME" #'を指定すると、ホームディレクトリが削除されます。


awkオプションフラグとして解釈される可能性のあるファイル名を含む、奇妙なファイル名の完全に保証された処理については、以下を使用します。

for f in *; do printf '%s\n' "$f"; awk '{print $1}' < "$f" | uniq; done
3
Wildcard

次の2つの主な問題があります。

  1. ls *xargsにパイピングするのは間違いです。 willファイル名にスペース、改行、シェルグロブ文字が含まれている場合、または(xargsで実行しているものに応じて)-で始まる場合は中断します。

    代わりにfind ... -print0 | xargs -0を使用してください。

  2. ネストされた引用。 @Gillesが彼の回答で言及しているように、これを正しく行う方法はいくつかありますが、ネストされた引用符のレベルが複数ある場合、very簡単に失われ、混乱します-成功したとしても、おそらく、明日は(簡単に)読み取ることも理解することもできないコードは、6か月後はもちろんです。

much必要なことを実行するスクリプトを記述してxargsで実行する方が簡単です。

スクリプトが複数のファイル名引数でスタンドアロンで動作する場合、xargsで動作し、-I {}-L 1を意味します。xargsのFreeBSDバージョンにも-Jオプションがあり、この問題を回避します)。

たとえば、myscript.sh

#! /bin/sh

for f in "$@" ; do
    echo "$f"
    awk '{ print $1 }' -- "$f" | uniq
done

(私が試したawkのほとんどのバージョンは、--がオプション引数の処理を停止することを意味することを意味します。original-awkは、freebsdのawkと同じではありません。もしawkがt、awkコマンドラインから削除するだけです)

そしてそれを次のように実行します:

./myscript.sh *

*はファイルだけでなくサブディレクトリにも一致することに注意してください。

またはそのように:

find . -maxdepth 1 -type f -print0 | xargs -0r /path/to/myscript.sh

または

find . -maxdepth 1 -type f -exec /path/to/myscript.sh {} +

これら2つは、現在のディレクトリにある通常のファイルのみを処理します。

入力ファイルが事前にソートされていない場合は、uniqではなくsort -uを使用してください。

0
cas