Bash에서 문자열 변수 행을 반복합니다.

Bash에서 문자열 변수 행을 반복합니다.

USB 장치 목록을 표시하는 데 사용하고 싶은 스크립트가 있습니다 lsblk.

주문하다:

$ lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb

이로 인해

sdb   usb    Kingston DataTraveler 2.0
sdc   usb    Kingston DT 101 G2 

나중에 사용할 수 있도록 결과를 변수에 저장하고 싶기 때문에 다음과 같이 씁니다.

$ usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)

내가 기대하는 것은 변수가 usbs위에 표시된 대로 두 개의 전체 행에 결과를 저장하는 것입니다. 하지만 내가 실행하면 :

for i in ${usbs[@]}; do
  echo $i
done

내가 얻은 결과는 단어로 나뉩니다.

sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2

질문:grep이 명령을 사용하여 명령 결과를 두 개의 전체 라인으로 저장하는 방법이 있습니까 ?

결과를 파일에 덤프한 다음 읽는 것보다 간단한 해결책이 있는지 알고 싶습니다.

답변1

이것은 좋은 사용 사례입니다배열/맵 파일 읽기:

readarray -t usbs < <(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)

그러면 각 행이 자체 요소로 나누어지는 출력 배열이 생성됩니다.

귀하의 경우 다음과 같은 배열이 생성됩니다.

usbs=(
'sdb   usb    Kingston DataTraveler 2.0'
'sdc   usb    Kingston DT 101 G2'
)

전체 출력을 배열 대신 단일 변수에 할당하면 기본적으로 다음과 같은 작업이 수행됩니다.

usbs='sdb   usb    Kingston DataTraveler 2.0
sdc   usb    Kingston DT 101 G2 '

배열로 만들려면 다음을 수행하십시오.

usbs=($(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb))

그러나 이렇게 하면 공백으로 구분된 각 단어가 다음과 같은 자체 요소가 됩니다.

usbs=(
sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2
)

여러 의견 작성자가 지적했듯이 일반적으로 항상 모범 사례이므로 변수를 인용해야 합니다. 이 경우 변수를 인용해야 하며 일반적으로 항상 인용해야 합니다.

우선, 이는 문제를 해결하는 올바른 방법이 아니라는 점을 말씀드리고 싶습니다. 그것은 "사람을 죽이면 안 된다. 그렇지 않으면 감옥에 가게 될 것이다"라고 말하는 것과 약간 비슷합니다.

마찬가지로 변수를 인용하면 보안 허점이 생기기 때문에 변수를 인용하지 않습니다. 그렇게 하지 않는 것이 잘못되었기 때문에 변수를 인용하고 있습니다(그러나 감옥에 대한 두려움이 도움이 될 수 있다면 왜 안 됩니까).

  • 스티븐 차제라스

경우에는 for i in ${usbs[@]}; do각각 i으로 설정됩니다.단어(공백으로 구분) in usbs. 이렇게 인용하면 각각으로 설정됩니다 for i in "${usbs[@]}"; do.i요소usbs, 예상대로 .

답변2

이건 대부분 사기에요새 줄과 bash 변수여기에는 배열이 포함되지 않습니다. 여기에서 여러 줄이 포함된 변수를 사용하려면 개행에서 매개변수 확장을 수행하고 와일드카드를 건너뛰어야 하며 데이터에 따라 다른 손상을 방지할 수 있습니다.

 usbs=$( lsusb ... )
 IFS=$'\n'  # ksh bash zsh; in other shells you may need to quote an actual newline
 set -o noglob  # or more tersely set -f
 for i in $usbs; do
   printf '%s\n' "$i" # not echo which sometimes modifies some data
 done
 # if you do further things in the same script (or function) you may 
 # need to re-set IFS and/or glob, which may require saving them first

배열의 경우 readarray/mapfileJesse_b의 제안이 이미 행으로 분할되어 있으므로 가장 쉽습니다. 하지만 당신은할 수 있는위와 같이 "수동으로" 수행하십시오.

set -o noglob  # ditto 
IFS=$'\n' usbs=( $( lsusb ... ) )  # only ksh up has arrays so $'' safe
# set +o noglob or set +f if needed
for i in "${usbs[@]}"; do # quoted array[@] forces splits equal to array elements only
  printf '%s\n' "$i"
done

답변3

grep질문: 명령을 사용하여 명령 결과를 두 개의 전체 라인으로 저장하는 방법이 있습니까 ?

예, 할당 코드가 정확합니다.

usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)

이는 정확히 필요한 작업을 수행합니다. 단일 변수(배열이 아님)에 lsblk개행 문자로 구분된 두 줄을 저장합니다. 그러나 for루프는 이 변수를 읽는 데 적합한 도구가 아닙니다. 루프가 while훨씬 더 좋습니다. 일부 독자에는 USB 장치가 연결되어 있지 않을 수 있으므로 가상 데이터의 예는 다음과 같습니다.

t=$(echo foo bar; echo baz bing;)
while read i ; do echo "$i" ; done <<< "$t"

산출:

foo bar
baz bing

더 짧은 방법은 다음과 같습니다.

xargs -L 1 <<< "$t"

참고: 평범하지만POSIX스타일 변수 x는 배열이 아니므로 배열 표기법을 사용하여 식별 bash할 수 있으며 배열이 아니라고 불평하지 않습니다. 데모:xx

x=f
echo $x ${x[0]} ${x[@]}

산출:

f f f

그러나 x배열은 아닙니다. 그렇다면 이 코드(사용bash 매개변수 변환 A옮기다연산자), 확실히 배열을 출력합니다:

echo "${x[@]@A}"

...그렇지 않습니다:

x='f'

비교를 위해 위의 모습과 어떻게 보이는지 비교해 보겠습니다.정렬. 먼저 매우 유사한 배열을 만든 y다음 사용하십시오.A옮기다차이점을 표시하는 연산자:

y=(f)
echo "${y[@]@A}"

산출:

declare -a y=([0]="f")

관련 정보