문자열을 공백 문자로 분할(기본 이름에 이상한 문제가 있음)

문자열을 공백 문자로 분할(기본 이름에 이상한 문제가 있음)

출력을 분리해야 하는데 ps간격이 있습니다.

#!/bin/bash

A=$(ps -r -e -o pcpu=,comm= | head -1)
B=${A[0]}
C=${A[1]}
printf '%3s  %s\n' $B $(basename $C)

출력은 다음과 같아야 합니다.

 42 bar

대신 나는 다음을 얻습니다.

usage: basename ...

이것이 작동하지 않는 이유는 무엇이며, 가장 중요하게는 어떻게 작동하게 만들 수 있습니까?

답변1

문자열을 공백 문자로 분할하려면 분할+글로브 연산자를 사용하십시오. 이는 명령 대체 또는 매개변수 확장 시 호출되지만 분명히스칼라최대 하나의 값을 저장할 수 있는 변수입니다.

 var=$(...)

스칼라 변수 할당입니다. 따라서 전체 출력은 $var와 동일하게 에 할당됩니다 ${var[0]}. 배열 변수 할당의 경우 다음과 같습니다.

 var=($(...))

이제 호출하기 전에 평소와 같이 조정해야 합니다.

 set -o noglob # disable the glob part which you don't want here
 IFS=' ' # split on space only
 var=($(some command)) # invoke it

나눠지니 참고하세요순서공백과 선행 및 후행 공백은 무시됩니다.

그런 다음 $BNor의 확장에서 호출하고 싶지 않으므로 인용해야 합니다.$C$(basename...)

 printf '%3s  %s\n' "$B" "$(basename -- "$C")"

--또한 옵션의 끝을 표시하는 것을 잊지 마십시오 .

주변 따옴표 $C( $C귀하의 경우 ${A[1]}값이 할당되지 않았기 때문에 비어 있음) 를 잊어버렸기 때문에 basename빈 인수를 전달하고 이에 대해 불평하는 대신 인수가 전달되지 않습니다.

답변2

다른 사람들은 코드에 오류가 무엇인지 알아차리고 초기 자리 표시자 데이터에 대해 배열이 더 나은 데이터 구조 선택이 될 것이라고 올바르게 제안했으며 문자열이 올바르게 분할되었는지 확인하는 방법 등도 있었습니다.

이제 구문 분석 중인 실제 명령이 무엇인지 알았으므로 개선 사항을 제안하는 데 더욱 창의적일 수 있습니다.

다음 스크립트는 명령 출력의 각 줄을 ps공백으로 구분된 두 비트로 읽습니다. 루프의 본문은 읽기 비트를 다양한 방식으로 출력합니다.

#!/bin/bash

ps -r -e -o pcpu=,comm= |
while IFS=' ' read -r pcpu comm; do
    printf 'pcpu=%s,\tcomm=%s,\tbasename of comm=%s\n' \
        "$pcpu" "$comm" "${comm##*/}"
done

여기에서는 comm출력에서 ​​첫 번째 공백 시퀀스 이후의 모든 내용이 저장됩니다 ps(첫 번째 열 앞의 초기 공백은 잘립니다).

원한다면 이를 초기 파이프라인의 일부로 삽입할 수 있습니다 head -n 1.

일부 셸(포함)에서는 bash루프가 하위 셸에서 실행되므로 그 안에 생성된 모든 변수는 파이프라인이 완료된 후에 사용할 수 없습니다. 이에 대한 두 가지 해결책이 있습니다 bash.

  1. lastpipe스크립트에서 셸 옵션을 사용하거나 활성화합니다.shopt -s lastpipe
  2. 절차적 대체를 통해 루프로 데이터를 읽습니다.

    while IFS=' ' read ...
       # ...
    done < <( ps ... )
    

예제를 실행하세요:

$ bash script.sh
pcpu=0.0,       comm=tmux,      basename of comm=tmux
pcpu=0.0,       comm=sh,        basename of comm=sh
pcpu=0.0,       comm=sh,        basename of comm=sh
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=bash,      basename of comm=bash
pcpu=0.0,       comm=ps,        basename of comm=ps
pcpu=0.0,       comm=sh,        basename of comm=sh

답변3

()를 사용하여 배열을 정의합니다. 큰따옴표를 사용하면 전체 문자열로 사용됩니다.

자세히 알아보기대량으로

$ cat test.sh
#!/bin/bash

A=(42 /foo/bar)
B=${A[0]}
C=${A[1]}
printf '%3s  %s\n' $B $(basename $C)

$ bash test.sh
 42  bar

답변4

awk를 사용하는 또 다른 방법:

 echo "42 /foo/bar" | awk '{n=split($2,b,"/"); print $1,b[n]}'

관련 정보