다음과 같은 bash 기능이 있습니다.
function exe {
echo -e "Execute: $1"
# Loops every 3s, outputting '...' until command finished executing
LOOP=0
while true;
do
if ! [ $LOOP == 0 ]; then echo -e "..."; fi;
sleep 3;
LOOP=$LOOP+1
done & ERROR="$($2 2>&1)" # Execute the command and capture output to variable
status=$?
kill $!; trap 'kill $!' SIGTERM
if [ $status -ne 0 ];
then
echo -e "✖ Error" >&2
echo -e "$ERROR" >&2
else
echo -e "✔ Success"
fi
return $status
}
목적은 다음과 같이 호출하는 것입니다.
exe "Update apt indexes" \
"sudo apt-get update"
어떤 출력:
Execute: Update apt indexes
...
...
...
...
✔ Success
전달된 명령에서 따옴표로 묶은 문자열을 매개변수로 사용하지 않으면 제대로 작동합니다.
예를 들어 다음은 작동하지 않습니다.
exe "Create self signed certificate" \
"sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt -subj \"/C=GB/ST=London/L=London/O=Company Ltd/OU=IT Department/CN=dev.domain.local\""
set -x는 위 명령이 실행을 위해 다음 명령으로 변환됨을 보여줍니다.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt -subj '"/C=GB/ST=London/L=London/O=Confetti' Celebrations Ltd/OU=IT 'Department/CN=dev.sign-in.confetti.local"'
이는 작은따옴표를 많이 선택하여 명령을 유효하지 않게 만드는 것 같습니다.
이 제한이 없는 버전을 원합니다. 어떤 아이디어가 있나요?
===============
제안 및 기타 버그 수정 후 최종 코드는 다음과 같습니다.
exe () {
echo -e "Execute: $1"
LOOP=0
while true;
do
if ! [ $LOOP == 0 ]; then echo -e "..."; fi;
sleep 3;
LOOP=$((LOOP+1))
done & ERROR=$("${@:2}" 2>&1)
status=$?
kill $!; trap 'kill $!' SIGTERM
if [ $status -ne 0 ];
then
echo -e "✖ Error" >&2
echo -e "$ERROR" >&2
else
echo -e "✔ Success"
fi
return $status
}
이 함수는 방랑 구성 쉘 스크립트의 "미화기"로 사용되며 다음과 같이 호출될 수 있습니다.
exe "Update apt indexes" sudo apt-get update
출력은 다음과 같이 나타납니다.
Execute: Update apt indexes
...
...
...
...
✔ Success
3초 미만 동안 지속되는 명령에는 진행률 출력이 표시되지 않습니다.
오류가 발생하지 않는 한 오류 상태와 명령의 전체 출력을 받게 됩니다.
주요 목적은 stderr에 메시지를 출력할 때 방랑 구성 스크립트에 의해 표시되는 빨간색 선을 제거하는 것입니다. 많은 명령은 메시지를 다른 명령으로 연결하지 않는 것이 목적이므로 stderr에 정보를 올바르게 출력합니다. Vagrant는 메시지를 표준 출력으로 인쇄합니다. 이렇게 하면 오류인 것처럼 보이지만 실제로는 오류가 아닌 많은 구성 메시지가 남을 수 있습니다.
이 함수는 실행된 명령이 0이 아닌 상태를 반환하지 않는 한 stderr에 인쇄하지 않습니다. 이는 명령이 실패를 나타내지 않는 한 빨간색 메시지가 표시되지 않음을 의미합니다. 명령이 0이 아닌 메시지로 실패를 나타내면 명령의 전체 출력을 stderr에 인쇄하여 빨간색 선을 제공합니다.
쉘 스크립트를 사용하면 방랑 구성이 훨씬 깔끔해지며 실제로 빨간색 메시지를 주의 깊게 관찰하고 그 의미를 알 수 있습니다.
위의 코드에서 놓친 일부 시각적 요소를 포함하여 vagrant와 함께 사용할 수 있는 전체 기능은 여기에서 볼 수 있습니다.https://gist.github.com/michaelward82/c1903f2b37a76975740e
답변1
전체 명령을 문자열로 전달하고 싶지 않을 수도 있습니다. 셸에는 매개변수 목록으로 목록이 있으며 목록을 목록으로 전달하는 것이 훨씬 간단합니다.
를 쓰는 대신 exe blah "blahh cmd"
처럼 명령어를 직접 작성하고, exe blah blahh cmd
전체 명령어를 직접 사용해야 할 때는 를 사용하세요.슬라이스 확장첫 번째 매개변수 다음의 모든 항목을 가져옵니다 ERROR=$("${@:1}" 2>&1)
.
shift
전통적으로 전체 매개변수 목록을 이동하려면 "왼쪽"을 사용할 수 있습니다 (참고자료 참조 help shift
).
f(){
local j="$1"
shift
echo "$j,$3"
shift 50
echo "$1" # guess what "$@" is now?
}
f {1..100}
그러나 이것은 분명히 bash에는 필요하지 않습니다.
슬라이싱에 대해 말하면 다음도 확인해 보세요.정렬배쉬에서.
어, 그래도.. 을 사용하여 문자열을 직접 실행할 수 있지만 eval
단순한 명령 이상의 기능을 허용하기 때문에 이는 일반적으로 나쁜 것으로 간주됩니다.
스타일 팁으로 및 xxx()
보다 짧고 function xxx
이식성이 더 높은 (POSIX-)을 선호합니다 function xxx()
. Bash에서는 동일합니다.
답변2
귀하의 질문의 핵심 질문은 "문자열을 분할하는 방법"입니다 $var
.
"악"(오류 및 코드 실행이 발생하기 쉽기 때문에) 방법은 eval을 사용하는 것입니다.
eval set -- $var ### Dangerous, not recommended, do not use.
이는 위치 인수에 분할 문자열을 설정합니다(배열은 약간 더 복잡합니다). 그러나 $var
변수를 인용 해제하면(무엇을 하고 있는지 실제로 알지 않는 한 어떤 희생을 치르더라도 피해야 함) "단어 분리"(우리가 원하는 것)가 발생하지만 "경로 이름 확장"도 발생할 수 있습니다. 이 명령을 시도해 볼 수 있습니다(파일이 거의 없는 디렉터리 사용).
$ var='hello * world'
$ eval set -- $var
$ echo "$@"
실행은 안전하며 외부에서 값이 설정되지 않으며 확장 프로그램은 *
위치 인수의 값만 설정합니다.
"경로 이름 확장"을 방지하기 위해 set -f
a가 사용되며, 이 경우 명령에 통합하기 쉽습니다.
$ var='hello * world'
$ set -f
$ eval set -- $var
$ echo "$@"
hello * world
기본 IFS는 입니다 spaceTabNew Line.
IFS를 외부에서 설정할 수 있으면 상황이 복잡해질 수 있습니다.
다음 방법을 사용하면 여러 가지 문제를 해결할 수 있습니다 read
.
$ IFS=' ' read -ra arr <<<"$var"
$ echo "${arr[@]}"
hello * world
이는 명령에 대한 IFS를 설정하고(IFS를 외부적으로 설정하지 않음) 읽을 때 백슬래시를 처리하지 않으며(-r 옵션) 모든 것을 배열 변수에 넣고(-a 옵션) 인용 변수를 사용합니다 "$var"
. 주의할 점은 단어 사이에 반복되는 공백이 제거된다는 것입니다(IFS는 공백이므로). 실행 가능한 명령줄에서는 문제가 되지 않습니다.
그러나 공백이 포함된 인수가 필요한 명령을 실행하려고 하면 실패합니다.
$ var='date -d "-1 day" +"%Y.%m.%d-%H:%M:%S"'
$ IFS=' ' read -ra arr <<<"$var"
$ "${arr[@]}"
date: extra operand `+"%Y.%m.%d-%H:%M:%S"'
유일한 실제 해결책은 처음부터 명령 배열을 올바르게 구축하는 것입니다.
$ arr=( date -d "-1 day" +"%Y.%m.%d-%H:%M:%S" )
$ "${arr[@]}"
2016.03.05-00:25:17
이 솔루션을 CSV "쉼표(공백)로 구분된 값"으로 생각하세요.
이 스크립트는 다음과 같이 작동합니다.
#!/bin/bash
function exe {
echo "Execute: $1"
# Loops every 3s, outputting '...' until command finished executing
LOOP=0
while true; do
if [ $LOOP -gt 0 ]; then echo -e "..."; fi;
sleep 3;
(( LOOP++ ))
done &
ERROR="$("${@:2}" 2>&1)" # Execute command and capture output.
status=$?
kill $!; trap 'kill $!' SIGTERM
if [ $status -ne 0 ];
then
echo "✖ Error" >&2
echo "$ERROR" >&2
else
echo "✔ Success"
fi
return $status
}
cmd=( date -d '-1 day' +'%Y.%m.%d-%H:%M:%S' )
exe "give me yesterday date" "${cmd[@]}"
cmd=( sudo apt-get update )
exe "update package list" "${cmd[@]}"
답변3
코드로 실행할 때 매개변수 문자열 내부에 따옴표가 있으면 매개변수 문자열을 위치 매개변수 배열과 같은 배열로 다시 구문 분석할 수 있습니다 $@
. 이는 적어도 주어진 예에서는 -를 사용하여 달성할 수 있습니다 ... & ERROR="$( printf "%s" "$2" | xargs sh -c 'exec "$0" "$@" 2>&1' ) ...
. (이미 인용된 문자열에 추가 큰따옴표가 있으면 xargs: unterminated quote
메시지가 나타날 수 있습니다.)
추가 조언은 다음을 참조하세요.Linux/Bash: 인용을 해제하는 방법은 무엇입니까?.
# test cases
# help :
#set -- '' "ls -ld / 'a bc'"
set -- '' ": sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt -subj \"/C=GB/ST=London/L=London/O=Company Ltd/OU=IT Department/CN=dev.domain.local\""
printf "%s" "$2" |
xargs sh -c '
echo "arg 0: ${0}"
for ((i=1; i<=$#; i++)); do
echo "arg $i: ${@:i:1}"
done
set -xv
"$0" "$@"
'
# output
arg 0: :
arg 1: sudo
arg 2: openssl
arg 3: req
arg 4: -x509
arg 5: -nodes
arg 6: -days
arg 7: 365
arg 8: -newkey
arg 9: rsa:2048
arg 10: -keyout
arg 11: /etc/apache2/ssl/apache.key
arg 12: -out
arg 13: /etc/apache2/ssl/apache.crt
arg 14: -subj
arg 15: /C=GB/ST=London/L=London/O=Company Ltd/OU=IT Department/CN=dev.domain.local
"$0" "$@"
+ : sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt -subj '/C=GB/ST=London/L=London/O=Company Ltd/OU=IT Department/CN=dev.domain.local'
(BTW LOOP=$LOOP+1
위의 코드에서는 이어야 합니다 LOOP=$((LOOP+1))
.)