간단한 변수에 필드 분할을 사용하는 것보다 배열을 사용하는 for 루프가 더 낫습니까?

간단한 변수에 필드 분할을 사용하는 것보다 배열을 사용하는 for 루프가 더 낫습니까?

여러 가지 응용 프로그램이 열려 있습니다. 달리기wmctrl출력을 파이프로 연결다음과 같이 창 ID("고정" 창 제외)를 나열합니다.

$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$ 

이 출력을 다음으로 보낼 수 있습니다.wmctrl이 창을 모두 닫으려면 다음을 수행하세요.

  • 저장해야 할 내용이 없는 창과 응답이 필요 없는 창은 묻지도 않고 닫히지만

  • 저장되지 않은 콘텐츠가 있는 편집자 창이나 프로세스를 실행 중인 터미널의 창은 "정상적으로" 닫힙니다. 각 응용 프로그램은 변경 사항을 저장하거나 변경 사항을 취소하거나 아직 실행 중인 프로세스에 대해 알릴 수 있는 창을 표시합니다.

적절한 바로가기에 할당된 다음 스크립트가 작동합니다.

#!/bin/bash

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')

for i in ${list[@]}
do
    wmctrl -i -a $i
    wmctrl -i -c $i
done

나는 (나에게) 더 간단한 방법 for i in $list도 효과가 있다는 것을 알았습니다.

둘 중 하나를 선호하는 이유가 있나요?


"끈끈한"과 "우아하게"는 의 용어입니다 man wmctrl.

답변1

귀하의 스크립트 $list에서는 ${list[@]}.

후자는 배열 구문이지만 스크립트에서는 일반 변수입니다.


출력 항목 에 공백이 없으므로 wmctl배열이 필요하지 않으며 사용하는 것이 $list완벽합니다.


그 경우~였다배열은 $list배열의 첫 번째 항목(=> item1) 일 뿐이며 ${list[@]}모든 항목으로 확장됩니다(=> item1 item2 item3).

하지만 실제로 당신이 원했던 것은~였다배열은 "${list[@]}"(따옴표 포함)로 확장되므로 "item1" "item2" "item3"공백으로 인해 질식하지 않습니다.


(읽다)

답변2

루프 는 명령 출력을 처리하는 데 루프 while보다 더 적합한 경우가 많으며 for, 이를 통해 라인을 목록에 저장하는 대신 직접 처리할 수 있습니다.또는정렬.

이 경우 명령을 완전히 피할 수 있습니다 awk.

wmctrl -l | while read -r id dt stuff; do 
  case $dt in 
    -1) continue
        ;; 
     *) echo wmctrl -i -a "$id"
        echo wmctrl -i -c "$id"
        ;; 
  esac
done

echo올바른 일을 하고 있다고 생각되면 s를 제거하세요 .

의견에서 언급했듯이 xargs또 다른 옵션입니다. 그러나 각각에 대해 두 가지 이상의 작업을 수행하려는 경우 까다로워집니다 arg.

답변3

원래 제목에 대한 답변

원래 제목에서는 "어떤 유형의 for 루프가 더 나은지"를 물었습니다.

나 자신에게는 가장 좋은 방법은 가장 빠른 방법이다. 이를 확인하려면 time스크립트나 함수 앞에 명령을 추가하세요. 몇 가지 예:

$ time du -s

real    0m0.002s
user    0m0.003s
sys     0m0.000s

$ time ls

real    0m0.004s
user    0m0.000s
sys     0m0.004s

하지만 테스트 사이에 캐시된 버퍼를 플러시하는 것이 중요합니다.

두 루프의 속도가 거의 같으면 가독성이 가장 좋은 루프를 선택하겠습니다.

이 질문의 범위는 대부분의 시간이 사용자 입력을 기다리는 데 소비되고 대부분의 사람들에게 최대 10개의 창만 열려 있기 때문에 속도와 관련이 없게 만드는 것입니다.


질문 본문에 대한 답변

다른 답변은 스크립트 다시 작성에 중점을 두므로 2센트 가치도 제공하겠습니다.

라인:

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
  • 의도가 배열인 경우 형식이 잘못되었습니다.
  • list일반적이며 설명적이지 않음

그래서 나는 다음을 사용할 것입니다 :

Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
  • ()의 외부 세트는 bash/shell 내부의 모든 것이 공백으로 구분된 배열 요소임을 알려줍니다.
  • Windows는 우리가 말하는 것이므로 설명적인 배열 이름입니다.
  • Windows는 복수형이므로 명명 규칙을 사용하면 배열인지 식별하는 데 도움이 됩니다.

라인:

wmctrl -i -a $i
  • -i으로 -a결합할 수 있습니다 -ia.
  • $i대신에 사용하겠습니다 $Window.

더 짧고 읽기 쉬운 스크립트를 작성하는 방법에는 두 가지가 있습니다. 먼저 배열을 사용합니다.

#!/bin/bash
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ' ) )
for Window in "${Windows[@]}" ; do wmctrl -ia $Window -c $Window ; done

배열이 없는 두 번째:

#!/bin/bash
Windows=$(wmctrl -l | awk ' !/-1/ { print $1 } ' )
for Window in $Windows ; do wmctrl -ia $Window -c $Window ; done

배열 방식을 선호하는 이유는 배열 방식에 대해 더 자세히 알아보고 최대한 활용하고 싶기 때문입니다. 그러나 선택은 귀하의 것입니다.

답변4

어레이 없이도 관리할 수 있습니다. 환경IFS줄 바꿈을 사용하면 라인을 반복할 수 있으므로 루프 자체에 영향을 주지 않고 루프 내부에서 IFS를 for수행할 수 있습니다 .unset

#!/bin/bash

IFS=$'\n'
for i in $(wmctrl -l); do
    unset IFS
    set -- $i
    (($2 > -1)) && wmctrl -i -a $1 -c $1
done

(재설정위치 매개변수라인을 필드로 분할하는 깔끔한 방법입니다.)

배열을 사용해야 하는 경우 사용할 수 있습니다.맵파일콜백 함수를 활용하여 루프와 유사한 것을 만듭니다. 소규모 반복 세트의 경우 더 간단한 함수 호출을 사용하는 것이 이점이 될 수 있습니다.

mapfile -c 1 -C 'f(){ set -- $@; (($3 >= 0)) && wmctrl -i -a $2 -c $2; }; f' -t < <(wmctrl -l)

(긴 버전):

#!/bin/bash

f(){
    set -- $@
    if (($3 > -1)); then
        wmctrl -i -a $2 -c $2
    fi
}
mapfile -c 1 -C f -t < <(wmctrl -l)

관련 정보