ディレクトリの下にあるすべての.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
が-print0 -printf "%f"
を2つの引数として正しく分割できないため、問題が発生しました。
例:
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
ご覧のとおり、xargs
は2つのパスを/mnt/tokenized/test.txt/mnt/test/test33.txt
で混合しているため、エラーNo such file or directory
が発生します。
それを機能させる方法は?
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
がnull区切りリストを解体するときが来たときに、正しい方法で解体できるように、フルパス名もnull区切り文字とともに渡す必要があります。
そうしないと、1つのファイルの完全パス名が次のファイルのベース名にマージされ、複数のファイル名の場合に観察された現象が発生します。
次に、一度に2つの引数をbash alligator
にフィードする必要があります。そうしないと、許可されている数だけ消費されますが、実行可能ファイル./thulac
には最初の2つだけが渡されます。
より良い代替策は、xargs
を省略して、すべての作業をfind
で行うことです。これは、xargsが一度に2つの引数を処理するため、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'
xargs
は、xargs
が不適切に処理されることで悪名高いため、常に '-'で始まり、ダブルスペース、 'および "を含む入力でソリューションをテストする必要があります。
mkdir -- '-" '"'"
seq 10 > ./-\"\ \ \'/'-" '"'".txt
GNU Parallelを使用したソリューションは次のとおりです:
find . -name "*.txt" -print0 |parallel -0 ./thulac '<' {} '>' {/}
<と>は、parallel
で始まるシェルによって解釈されるため、引用符で囲む必要があります。代わりに、parallel
で始まるシェルによって解釈されるようにします。
問題があるのはあなたのfindコマンドです。
2つの名前を区切るには、printf形式でスペースを含めます
find /mnt/test -name "*.txt" -print0 -printf " %f\n"
^ ( note the space above)
スクリプトが何を達成する必要があるかを正確に伝えることはできませんが、各奇数ファイルを最初の引数として渡し、各偶数ファイル名を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 and = GNU xargs:
find /tmp/test -name "*.txt" -exec sh -c '
./thulac < "$1" > "/mnt/tokenized/$(basename "$1")"' sh {} \;
ご了承ください {}
は、fish
シェルを使用する場合にのみ引用する必要がありますが、これは非常にまれな状況です。