병렬 변수 선언 sh -c…

병렬 변수 선언 sh -c…

find나는 with 의 출력을 처리하려고 시도했는데 parallel, 이는 차례로 쉘을 호출합니다(일부 텍스트 대체 필요). 나는 스스로 설명할 수 없는 이상한 행동을 관찰했습니다.

각 디렉토리에는 여러 개의 파일이 있습니다 file1.xtc. file2.xtc그 중 일부는 file1.part0002.xtc. 전달된 파일에 find해당 이름이 있는 경우 결과 명령이 다음과 같이 보이도록 *.part000x.*해당 비트를 제거해야 합니다 .*.part000x.*

command -f file1.part0001.xtc -s file1.tpr 

findand 를 사용하여 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와 관련이 없습니다. 사실, parallelrun 이라고 말한 이후에는 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본질적으로 일회성이며 nameset 에 의해 유지 관리되는 환경에서 설정되고 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이 옵션은 권장되지 않습니다.

관련 정보