
.txt
ディレクトリ内のすべてのファイルのフルパスとファイル名を見つけて実行可能ファイルに渡したいと思います./thulac
。
到達するのに時間がかかりました。
find /mnt/test -name "*.txt" -print0 |xargs -l bash -c './thulac < $0'
しかし、これはフルパスのみを探します。
~から複数のパラメータを持つxargs 、理解します:
echo argument1 argument2 argument3 | \
xargs -l bash -c 'echo this is first:$0 second:$1 third:$2' | xargs
私が達成したいことは次のとおりです
find /mnt/test -name "*.txt" -print0 -printf "%f" | \
xargs -0 bash -c './thulac < $0 > $1'
ただし、ここではxargs
複数のファイルがある場合、2つのパラメータに正しく分割されていないため、混乱しています。-print0 -printf "%f"
例:
find /mnt/test -name "*.txt" -print0 -printf "%f" | \
xargs -0 -I bash -c './thulac < $0 > /mnt/tokenized/$1'
/mnt/test
上記のコマンドは、ファイルが1つしかない場合でも機能します。ただし、
/mnt/test
言語に関係なく複数のファイルがある場合:[root@localhost THULAC]# ls /mnt/test test33.txt test.txt [root@localhost THULAC]# find /mnt/test -name "*.txt" -print0 -printf "%f" | \ xargs -0 bash -c './thulac < $0 > /mnt/tokenized/$1' /mnt/test/test.txt: /mnt/tokenized/test.txt/mnt/test/test33.txt: No such file or directory
ご覧のとおり、2つのパス
xargs
が混在して/mnt/tokenized/test.txt/mnt/test/test33.txt
エラーが発生しますNo such file or directory
。
どのように機能させるのですか?
答え1
find /tmp/test -name '*.txt' \
-exec bash -c './thulac < "$(readlink -f {})" > "/mnt/tokenized/$(basename {})"' \;
find を使用してファイルを検索し、結果に対してコマンドを実行します。この方法でbash -c 'command'
複数の$()を実行できます。
readlink -f {}
結果を生成するために使用されるフルパス。
basename {}
結果からパスを削除するために使用されます。
答え2
作業するときは、xargs
常に「-」で始まり、二重スペース「and」を含む入力でソリューションをテストする必要があります。これはxargs
、これらの問題に対処するために悪名高いからです。
mkdir -- '-" '"'"
seq 10 > ./-\"\ \ \'/'-" '"'".txt
GNU Parallelを使用するソリューションは次のとおりです。
find . -name "*.txt" -print0 |parallel -0 ./thulac '<' {} '>' {/}
<と>は引用符で囲む必要があります。それ以外の場合は、起動シェルで解釈されますparallel
。私たちはそれらを開始シェルによって解釈したいと思いますparallel
。
答え3
find /mnt/test -name "*.txt" -print0 -printf "%f\0" |
xargs -0 -n 2 bash -c 'shift $1; ./thulac < $1 > /mnt/tokenized/$2' 2 1
また、空の区切り文字を使用してフルパス名を渡して、空の区切りリストを解体する必要があるときにxargs
正しい方法でこれを実行できるようにします。
それ以外の場合は、1つのファイルのフルパス名が次のファイルのデフォルト名にマージされ、これは複数のファイル名で観察される現象です。
その後、一度に2つのパラメータを指定する必要があります。bash alligator
それ以外の場合は、できるだけ多くのパラメータを使用しますが、実行可能ファイルには最初の2つのパラメータのみが渡されます./thulac
。
より良いオプションは、xargsが一度に2つの引数を処理するため、xargsxargs
ですべての操作を実行することです。このバージョンではこれを行うのではなく、フルパス名を提供し、ファイル名を直接計算します。find
xargs
bash
bash
find
find /mnt/test -name "*.txt" -exec bash -c './thulac < "$1" \
> "/mnt/tokenized/${1##*/}"' {} {} \;
問題の源
1. Good case when only 1 file present
-print0 -printf '%f'
/mnt/test/test.txt\0test.txt
|-----------------|--------|
arg0 = /mnt/test/test.txt
arg1 = test.txt
bash -c 'thulac < $0 > /mnt/tokenized/$1'
thulac < /mnt/test/test.txt > /mnt/tokenized/test.txt
2. Error case when > 1 file present
-print0 -printf '%f'
/mnt/test/test.txt\0test.txt/mnt/test/test33.txt\0test33.txt
|-----------------|-----------------------------|----------|
arg0 = /mnt/test/test.txt
arg1 = test.txt/mnt/test/test33.txt
arg2 = test33.txt
bash -c 'thulac < $0 > /mnt/tokenized/$1'
thulac < /mnt/test/test.txt > /mnt/tokenized/test.txt/mnt/test/test33.txt
固定する
We saw that the mixup occurred due to the absence of the delimiter '\0' in the -printf "%f"
So the correct way is:
find ... -print0 -printf "%f\0" | xargs ...
Ensuring that the list is partitioned at the right places and the
sequence of fullpath1+file1\0fullpath2+file2\0... is maintained.
Now coming to the 'xargs' part, we write:
xargs -0 -n 2 bash -c '...' 2 1
Points to observe are the following:
a) '-0' => arguments to xargs will be taken to be NULL separated.
b) -n 2 => we feed 2 args at a time to bash from the total pool
delivered to xargs by find.
c) 2 1 is just a best practice to get over different shell's behavior
regarding what construes as $0, $1, $2, ...; In your particular case since you
already know that $0 -> first arg, $1 -> 2nd arg, we could just as well have
written what you did:
find ... | xargs -0 -n 2 bash -c './thulac < $0 > /mnt/tokenized/$1'
答え4
スクリプトがどのように実装するべきかを正確に言うわけではありませんが、すべての奇数ファイルを最初の引数として渡し、すべての偶数ファイル名を2番目の引数として渡すと仮定すると、移植可能な方法でこれを行う方法は次のとおりです。
t=$(mktemp)
find /tmp/test -name "*.txt" -exec sh -c '
if [ -s $1 ]
then
./thulac < "$(<$1)" > "/mnt/tokenized/$2"
else
printf "%s" "$2" > "$1"
fi' sh $t {} \;
rm $t
見つかったすべてのファイルのパスとファイル名のみを渡す場合は、答えは簡単です。それでもポータブルコマンドと構文(POSIX)のみを使用してください。つまり、bash、GNU find、GNU xargsに依存しません。
find /tmp/test -name "*.txt" -exec sh -c '
./thulac < "$1" > "/mnt/tokenized/$(basename "$1")"' sh {} \;
引用は{}
シェルを使用する場合にのみ必要です。fish
これはごくまれなシナリオです。