bash에서 문자열을 연결하여 빌드 명령


13

한 번에 실행하기 전에 일부 매개 변수를 기반으로 문자열로 명령 줄을 작성하는 bash 스크립트가 있습니다. 명령 문자열에 연결된 부분은 각 구성 요소를 통한 데이터의 "스트리밍"을 용이하게하기 위해 파이프로 구분됩니다.

매우 간단한 예 :

#!/bin/bash
part1=gzip -c
part2=some_other_command
cmd="cat infile"

if [ ! "$part1" = "" ]
then
    cmd+=" | $part1"
fi


if [ ! "$part2" = "" ]
then
    cmd+=" | $part2"
fi


cmd+="> outfile"
#show command. It looks ok
echo $cmd
#run the command. fails with pipes
$cmd

어떤 이유로 파이프가 작동하지 않는 것 같습니다. 이 스크립트를 실행하면 일반적으로 명령의 첫 번째 부분 (첫 번째 파이프 이전)과 관련된 다른 오류 메시지가 표시됩니다.

그래서 내 질문은 이런 식으로 명령을 작성할 수 있는지 여부와 가장 좋은 방법은 무엇입니까?


오류 메시지는 무엇입니까?
CameronNemo

내 스크립트에서 (이 단순화보다 다소 복잡합니다) "파일을 찾을 수 없습니다"
Lennart Rolland

infile현재 디렉토리에 있다고 가정해도 안전 합니까?
saiarcot895

예. 내 코드에서는 파일 대신 wget -O-입니다. 난 그냥 연결된 문자열을 복사하여 터미널에서 그것을 pasete 경우 사실, 그것을 잘 실행
레나 롤랑

답변:


17

모든 것은 상황이 언제 평가되는지에 달려 있습니다. 입력하면 $cmd나머지 줄 전체가 첫 번째 단어의 인수로 전달됩니다 $cmd.

walt@spong:~(0)$ a="cat /etc/passwd"
walt@spong:~(0)$ b="| wc -l"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
cat /etc/passwd | wc -l
walt@spong:~(0)$ $c
cat: invalid option -- 'l'
Try 'cat --help' for more information.
walt@spong:~(1)$ eval $c
62
walt@spong:~(0)$ a="echo /etc/passwd"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
echo /etc/passwd | wc -l
walt@spong:~(0)$ $c
/etc/passwd | wc -l
walt@spong:~(0)$ $c |od -bc
0000000 057 145 164 143 057 160 141 163 163 167 144 040 174 040 167 143  
          /   e   t   c   /   p   a   s   s   w   d       |       w   c  
0000020 040 055 154 012  
              -   l  \n  
0000024
walt@spong:~(0)$ eval $c
1  

echo명령에 전달 된 인수는 " /etc/passwd", " |"(세로 막대 문자), " wc"및 " -l"입니다.

보낸 사람 man bash:

eval [arg ...]  
    The  args  are read and concatenated together into   
    a single command.  This command is then read and  
    executed by the shell, and its exit status is returned  
    as the value of eval.  If there are no args, or only null  
    arguments, eval returns 0.

8

나중에 참조 할 수있는 한 가지 해결책은 "eval"을 사용하는 것입니다. 이렇게하면 bash가 문자열을 해석하는 방식을 잊어 버리고 모든 것을 마치 쉘에 직접 입력 한 것처럼 읽습니다 (정확히 원하는 것입니다).

위의 예에서

$cmd

eval $cmd

그것을 해결했다.


인용 된 매개 변수에주의하십시오. eval foo "a b"와 동일합니다 eval foo "a" "b".
udondan

2

@waltinator는 이미 이것이 예상대로 작동하지 않는 이유를 설명했습니다. 또 다른 방법은 bash -c명령을 실행하는 데 사용 하는 것입니다 .

$ comm="cat /etc/passwd"
$ comm+="| wc -l"
$ $comm
cat: invalid option -- 'l'
Try 'cat --help' for more information.
$ bash -c "$comm"
51

1
Parsimony는로 다른 프로세스를 시작하지 말고 현재 프로세스에서 명령을 수행하는 데 bash -c사용 eval합니다.
waltinator 2016 년

@waltinator는 물론 eval을 사용할 것입니다 (그래서 당신과 Lennart를 찬성했습니다). 방금 대안을 제공하고 있습니다.
terdon 2018 년

0

아마도 이것을하는 더 좋은 방법 eval은 Bash 배열을 사용 하지 않고 그냥 사용하는 것을 피하고 모든 인수를 작성하고 명령에 대해 실행하는 인라인 확장입니다.

runcmd=() # This is slightly messier than declare -a but works
for cmd in $part1 $part2 $part3; do runcmd+="| $cmd "; done
cat infile ${runcmd[@]} # You might be able to do $basecmd ${runcmd[@]}
# but that sometimes requires an `eval` which isn't great
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.