문제의 근원

문제의 근원

.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"


예:

find /mnt/test -name "*.txt" -print0 -printf "%f" | \
   xargs -0 -I bash -c './thulac < $0 > /mnt/tokenized/$1'
  1. /mnt/test위 명령은 파일이 하나만 있는 경우에도 작동합니다.

  2. 그러나 /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두 경로가 함께 혼합되어 /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 올바른 방법으로 이를 수행할 수 있도록 하려고 합니다.

그렇지 않으면 한 파일의 전체 경로 이름이 다음 파일의 기본 이름으로 병합되며, 이는 여러 파일 이름에서 관찰되는 현상입니다!

그런 다음 에 한 번에 2개의 매개변수를 제공해야 합니다. bash alligator그렇지 않으면 가능한 한 많은 매개변수를 사용하지만 실행 파일에는 처음 두 개의 매개변수만 전달됩니다 ./thulac.

더 나은 옵션은 xargs가 한 번에 2개의 인수를 처리하므로 xargs xargs에서 모든 작업을 수행하는 것 입니다. 이 버전에서는 이를 수행하는 대신 전체 경로 이름을 제공 하고 파일 이름을 직접 계산합니다 .findxargsbashbashfind

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

스크립트가 구현해야 하는 것이 무엇인지 정확히 말하지는 않지만 모든 홀수 파일을 첫 번째 인수로 전달하고 모든 짝수 파일 이름을 두 번째 인수로 전달한다고 가정하면 이식 가능한 방식으로 이를 수행하는 방법은 다음과 같습니다.

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 {} \;

인용 은 {}shell 을 사용할 때만 필요하며 fish이는 극히 드문 시나리오입니다.

관련 정보