パターンと解析されたパスにスペースが含まれている場合にワイルドカード/グローバル拡張を実行する方法は?

パターンと解析されたパスにスペースが含まれている場合にワイルドカード/グローバル拡張を実行する方法は?

POSIX shまたはBashを使用していくつかのパスを拡張する必要があります。

以下は2つのパターン例です(複雑すぎるように選択しました)。

$ npm pkg get workspaces | jq -r '.[]'
apps/app*
lib/{be,fe *} lib/*lib

私のディレクトリツリーが次のようになるとしましょう。

$ mkdir -p "lib/be lib/fantastic lib" "lib/fantastic" "lib/fe 1 lib/other lib" "apps/app1" "apps/app2" "be" "1"

$ tree
.
├── 1
├── apps
│   ├── app1
│   └── app2
├── be
└── lib
    ├── be lib
    │   └── fantastic lib
    ├── fantastic
    └── fe 1 lib
        └── other lib

12 directories, 0 files

パターンに一致するすべてのパスの簡単なリスト(1行に1つのパス)を取得するにはどうすればよいですか?

デフォルトのシェル拡張は、個々のパスを引用せずにパスを解析し、スペースで区切るように見えます。

たとえば、この偶数の一致は何ですか?

$ echo "lib/"{"be","fe "*}" lib/"*"lib"
lib/be lib/fantastic lib lib/fe 1 lib/other lib

それは次のようにすることができますlib/be lib/fantasticliblib/fe 1lib/other lib
lib/be lib/fantastic liblib/fe 1 lib/other lib
lib/be lib/fantastic lib lib/fe 1 lib/other lib

どのスペースが区切り文字で、どのスペースがパスの一部かを知らない場合は、不明のようです。

しかし、同様に難しいのは、スペースを含むすべての項目を引用する必要がありますが、同時にワイルドカードなどを引用してはいけません。

私の言葉は、私が何かを一緒に投げることができましたが、これが実際に可能なすべてのケースを解決するかどうか疑問です。

echo 'lib/{be,fe *} lib/*lib' | sed -e 's/\([*,{}]\)/"\1"/g' -e 's/.*/"&"/' -e 's/""//g'

両方のモードで実行すると動作するようです。

$ echo -e 'lib/{be,fe *} lib/*lib\napps/app*' | sed -e 's/\([*,{}]\)/"\1"/g' -e 's/.*/"&"/' -e 's/""//g' | while IFS= read -r line; do bash -c "echo $line"; done
lib/be lib/fantastic lib lib/fe 1 lib/other lib
apps/app1 apps/app2

それでは、道はどこから始まり、どこで終わりますか?

最後にevalまたはbash -c。たとえば、同様のファイルパターンでbye && rm -rf ~ホームディレクトリを削除できます。

ベストアンサー1

デフォルトのシェル拡張はパスを解析し、スペースで区切るように見えます。

それは愚かなことではなく、単に動作しません。ここで重要なのは、コマンドラインが処理されると、1つの長い文字列ではなく、さまざまな文字列(「単語」または「フィールド」)のセットのように処理されることです。中かっこ拡張とファイル名グローバルは、いくつかの異なるフィールドを生成します。これらのフィールドは、実行するすべてのコマンドのコマンドライン引数として使用されます(最終的にargv[]Cプログラムで通常呼び出される配列の要素として使用されます)。

問題であり、一般的なトラップは、echo取得するすべての引数をスペースで連結して、表示される長いリストを生成することです。

たとえば、Bash の相互作用は、help echoこれが正確に実行される操作であることを明示的に明示しています。

$ help echo
echo: echo [-neE] [arg ...]
    Write arguments to the standard output.

    Display the ARGs, separated by a single space character and followed by a
    newline, on the standard output.

これは、パラメータが明らかに異なる場合でも同じ出力を提供することを意味します。

$ echo foo bar doo
foo bar doo
$ echo "foo bar" doo
foo bar doo

しかし、このように単純なものを使用すると、どのようにls機能するかを確認できます。

$ touch "foo bar" doo
$ ls -l *oo*
-rw-r----- 1 ilkkachu ilkkachu 0 Sep  6 12:58 doo
-rw-r----- 1 ilkkachu ilkkachu 0 Sep  6 12:58 foo bar

echoglob 軸の出力をシェルにコピーすると、次のいずれかの結果が得られます。

$ ls -l foo bar doo
ls: cannot access 'foo': No such file or directory
ls: cannot access 'bar': No such file or directory
-rw-r----- 1 ilkkachu ilkkachu 0 Sep  6 12:58 doo

または

$ ls -l "foo bar doo"
ls: cannot access 'foo bar doo': No such file or directory

(文字列をスペースでさらに分割するかどうかによって異なります)

ここで解決策は、echoデバッグの使用を中止することです。代わりにprintf適切なオプションを使用してください。これは<、印刷と使用の間の異なるパラメータに対してフォーマット文字列を複数回再使用する必要があるという事実を考慮します。>printf

$ printf "<%s>\n" *oo*
<doo>
<foo bar>

または、次のスクリプトを作成します。

#!/bin/sh
printf "%d args\n" "$#"
if [ "$#" -gt 0 ]; then
    printf "<%s>\n" "$@"
fi

たとえばargs.sh。次に、スタンド拡張装置を試してみてください。

しかし、同様に難しいのは、スペースを含むすべての項目を引用する必要がありますが、同時にワイルドカードなどを引用してはいけません。

あなたは本当にこれから抜け出すことはできません。一部の文字は一方の面で特別であり(スペースは単語を分割します)、一部の文字は別の面で特別であり(glob文字はファイル名に拡張されます)、そのまま維持したいもの(glob文字)、維持したくない文字(スペース)。

最後に、evalまたはbash -cを使用して問題を解決する方法がわかりません。悪意のあるパターンがシステムを本質的に消去する可能性があるため、これはやや危険に見えます。

はい、危険ではありません。データをデータとして、コードをコードとして保持し、混在させないでください。ファイル名拡張は実際には分離を維持し、ワイルドカードを使用して任意の文字を含むファイル名を安全に処理できます。stdout問題は、複数のファイル名を単一の文字列または単一の出力ストリーム(たとえば、of)として印刷しようとしたときに発生しますecho。必要でない場合はこれを避け、そうする場合はファイル名をNULで終わる(Cスタイル)文字列で印刷してください。なぜならそれがNULだからです。

あなたの質問はトークン化(引用符なしのパラメータ拡張)に関するものではありませんが、まだ役に立つかもしれません。 https://mywiki.wooledge.org/WordSplitting

おすすめ記事