xargs sh -cで他のコマンドでawkを使用するための正しい構文

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

答え1

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 *(私の考えでは、あなたの言葉にならない命令は、非常に単純化された例にすぎません。)

答え2

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

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

  1. パイプの入り口が完全に間違っls *ていますxargs。それ〜するファイル名にスペース、改行文字、シェルワイルドカードが含まれているか(実行項目に従ってxargs-

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

  2. 入れ子になった引用符。 @Gillesが彼の答えで述べたように、これを正しく実行する方法はいくつかありますが、非常に複数の重複したネストされた引用符があると、迷子になったり混乱したりする可能性があります。成功しても、今から6ヶ月は苦労し、明日のコードを(簡単に)読んだり理解できないことがあります。

これはたくさん目的のタスクを実行するスクリプトを作成し、xargsを使用して実行する方が簡単です。

スクリプトが複数のファイル名引数とは独立して動作する場合は、xargsそれを使用せずに動作します-I {}(つまり、-L 1.FreeBSDバージョンxargsにも問題を回避するオプションがあることを意味します-J)。

たとえば、myscript.sh:

#! /bin/sh

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

awk私が理解しようとしたほとんどのバージョンは、--処理停止オプションargsを意味します。これはoriginal-awkfreebsdと同じではありません。そうでない場合は、コマンドラインから削除してください。)awkawkawk

次のように実行してください。

./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つは、現在のディレクトリの通常のファイルのみを処理します。

入力ファイルがあらかじめソートされていない場合のsort -u代わりにuniq

関連情報