find
나는 with 의 출력을 처리하려고 시도했는데 parallel
, 이는 차례로 쉘을 호출합니다(일부 텍스트 대체 필요). 나는 스스로 설명할 수 없는 이상한 행동을 관찰했습니다.
각 디렉토리에는 여러 개의 파일이 있습니다 file1.xtc
. file2.xtc
그 중 일부는 file1.part0002.xtc
. 전달된 파일에 find
해당 이름이 있는 경우 결과 명령이 다음과 같이 보이도록 *.part000x.*
해당 비트를 제거해야 합니다 .*.part000x.*
command -f file1.part0001.xtc -s file1.tpr
find
and 를 사용하여 parallel
이 효과를 얻었지만 parallel
대체 항목(특히 비트 {.}
)이 충분하지 않았습니다( .xtc
확장자를 제거하고 .part0001
그대로 두었습니다). 따라서 출력을 확인하는 데 사용한 명령은 다음과 같습니다.
find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name=""; name="{.}"; echo {.} ${name%.*}.tpr'
위 명령을 사용하여 먼저 이를 선언 name
하고 빈 문자열(또는 해당 문제에 대한 다른 항목)을 할당하면 결과는 다음과 같습니다.
file1.part0001 file1.tpr
필요에 따라(이것은 내 명령이 사용해야 하는 이름입니다) 그러나 이것을 실행하면
find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'
결과 :
file1.part0001 .tpr
$name
아니면 존재하지 않는 것처럼 작동합니다 .
그래서 내 질문은 다음과 같습니다
- 이런 행동을 하는 이유는 무엇인가?
- 이 문제를 처리하는 데 선호되는 방법은 무엇입니까?
여기서 첫 번째 질문이 더 중요합니다. 위에서 사용한 방법은 보기 좋지는 않지만 작동하는 해결 방법이기 때문입니다. 이런 식으로 텍스트를 교체해야 했던 것은 이번이 처음이 아니며, 이 동작은 여전히 나를 당혹스럽게 합니다.
산출sh --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)
bash
위 명령에서 대신 설치하고 사용한 최신 버전의 출력 sh
(동일한 효과를 얻기 위해)( /usr/local/bin/bash --version
)
GNU bash, version 4.2.0(1)-release (i386-apple-darwin11.4.2)
답변1
귀하의 문제는 bash와 관련이 없습니다. 사실, parallel
run 이라고 말한 이후에는 sh
그것을 사용하지 않을 수도 있습니다 bash
.
문제는 설명서에서 알 수 있듯이 병렬이 xargs를 실제로 대체하지 않는다는 것입니다. 대신, 인수를 단일 문자열(사이에 공백 포함)로 누적한 다음 일련의 명령으로 해석합니다. 따라서 귀하의 경우에는 다음이 있습니다.
sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'
이는 다음과 같이 해석됩니다.
sh -c 'name="{.}";
echo {.} ${name.*}.tpr
이는 두 개의 개별 명령이고 첫 번째 명령 sh -c
은 서브쉘( )에서 실행되므로 $name
두 번째 명령에는 설정되지 않습니다.
이제 문자열 시작 부분에 무엇이든 추가할 수 있습니다 true
. 예를 들면 다음과 같습니다.
sh -c 'true; name="{.}"; echo {.} ${name%.*}.tpr'
이는 다음과 같이 해석됩니다.
sh -c 'true'
name="{.}"
echo {.} ${name%.*}.tpr'
이 경우 호출은 sh
본질적으로 일회성이며 name
set 에 의해 유지 관리되는 환경에서 설정되고 parallel
마지막으로 set echo
으로 호출됩니다 name
.
따라서 가장 간단한 해결책은 불필요한 호출을 제거하는 것 같습니다 sh
.
find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 'name={.}; echo {.} "${name%.*}.tpr"'
노트:@StephaneChazelas가 제공한 팁에 따라 주변 따옴표를 제거 {.}
하고 주위에 추가했습니다 ${name%.*}.ptr
. 병렬 참조는 자체 대체를 사용하므로 이상한 방식으로 명시적 참조를 방해합니다. 그러나 대체 항목이 토큰화될 가능성이 있는 경우 추가해야 하는 셸 대체 항목에는 따옴표를 추가하지 않습니다.
어떤 이유로든 서브쉘(또는 특정 서브쉘)을 정말로 사용하고 싶다면, 또 다른 옵션은 다음을 사용하는 것입니다 -q
:
find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 -q sh -c 'name="{.}"; echo "{.}" "${name%.*}.tpr"'
노트:위에서 언급했듯이 제안을 조정했습니다. 이 경우 -q
대체 항목에 대한 참조는 명시적으로 금지되므로 명시적으로 참조해야 합니다. 그러나 이는 쉘 인용만큼 정확하지 않은 텍스트 인용입니다. 대체 인용문에 큰따옴표 문자가 포함되어 있으면 해당 문자는 이스케이프되지 않으므로 명시적인 인용문이 꺼지고 명령줄이 중단되어 효과적으로 삽입됩니다. 명령 주입 취약점( $
, `
또는 \
문자가 포함된 파일 이름). 이러한 이유로 무엇보다도 -q
이 옵션은 권장되지 않습니다.