여러 다른 서버에 연결된 postgresql 데이터베이스 CLI의 여러 인스턴스에 명령을 브로드캐스트하는 데 사용하는 이와 같은 스크립트가 있습니다. 하드코딩된 프로세스 교체 세트를 사용하고 있습니다.
#!/bin/bash
# names have been changed to protect the guilty
cred="user=dbadmin password=SECRET"
domain=example.com
tee \
>( psql -X "host=db1.$domain dbname=db1 $cred" ) \
>( psql -X "host=db2.$domain dbname=db2 $cred" ) \
>( psql -X "host=db3.$domain dbname=db3 $cred" ) \
>( psql -X "host=xdb1.$domain dbname=xdb1 $cred" ) \
> /dev/null
wait
내가 하고 싶은 것은 for 루프를 사용하여 대체 배열을 만들고 다음과 같이 해당 배열을 tee에 전달하는 것입니다.
tee "${p[@]}" > /dev/null
하지만 루프를 사용하면 tee가 각 항목에 대해 오류를 제공하기 $p
때문에 각 항목을 얻습니다./dev/fd/63
tee: /dev/fd/63: No such file or directory
작동하지 않는 코드 예:
p=()
for z in db1 db2 db3 xdb1
do
p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
done
tee "${p[@]}" > /dev/null
이 작업을 수행할 수 있는 방법이 있나요?
답변1
이는 다음 줄에서 발생합니다.
p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
...bash는 이 줄을 완전한 명령으로 간주합니다. 프로세스 교체가 발생하면 명령이 완료되면 교체된 프로세스의 STDIN이 닫힙니다.
내 생각에는 이 작업을 수행하는 방법이 두 가지밖에 없습니다.
eval
. 거기 가지 말자.exec
. 거기로 가자:
p=()
for z in db1 db2 db3 xdb1
do
exec {fd}> >(psql -X "host=$z.$domain dbname=$z $cred")
p+=( $fd )
done
cd /dev/fd && exec tee "${p[@]}" >/dev/null
이 {fd}>
구문을 사용하면 bash는 새 파일 설명자를 할당하고 해당 값을 할당한 $fd
다음 이를 푸시합니다 $p
.
이제 우리가 작성 $p
해야 할 파일 설명자 번호가 많이 있으므로 파일 설명자 번호를 실제 파일에 쓴 다음 를 호출합니다 .tee
cd
/dev/fd
tee
(고양이 가죽을 벗기는 방법은 여러 가지가 있지만 이것이 가장 먼저 떠오르는 가장 쉬운 방법입니다)
답변2
다음은 내 의견에서 내가 의미하는 바를 보여주는 간단한 예입니다.
for 루프(각 psql 명령은 성공할 때까지 반복되며 백그라운드 서브셸에서 실행됩니다). 생성된 프로세스는 파일 설명자 배열을 교체할 필요가 없습니다.
cred='user=dbadmin password=SECRET'
domain='example.com'
tf=$(mktemp)
cat > "$tf"
for z in db1 db2 db3 xdb1 ; do
( while ! psql -X "host=$z.$domain dbname=$z $cred" < "$tf" ; do : ; done) &
done > /dev/null
# don't delete the tempfile until all jobs have completed
wait
rm -f "$tf"
각 재시도 사이에 약간의 지연을 두려면 루프 대신 sleep x
선택적 으로 사용합니다 .:
while
더 똑똑한 버전은 while 루프 내에서 더 많은 작업을 수행하고 종료 상태 및/또는 grep stderr을 확인하여 오류의 원인을 찾고 적절하게 대응합니다.