sh에서 여러 줄의 변수를 나란히 인쇄하는 방법은 무엇입니까?

sh에서 여러 줄의 변수를 나란히 인쇄하는 방법은 무엇입니까?

이 질문은 다음을 기반으로 합니다.Ask Ubuntu에도 비슷한 질문이 있습니다, 하지만 bash에서 비슷한 출력을 원합니다 sh.

Bash에는 문제가 없습니다. 예상대로 작동합니다.

wolf@linux:~$ echo $SHELL
/usr/bin/bash
wolf@linux:~$ 
wolf@linux:~$ varA='Aug 01
> Aug 16
> Aug 26'
wolf@linux:~$ 
wolf@linux:~$ varB='04:25
> 03:39
> 10:06'
wolf@linux:~$ 
wolf@linux:~$ echo "$varA $varB"
Aug 01
Aug 16
Aug 26 04:25
03:39
10:06
wolf@linux:~$
wolf@linux:~$ paste <(printf %s "$varA") <(printf %s "$varB")
Aug 01  04:25
Aug 16  03:39
Aug 26  10:06
wolf@linux:~$

그러나 에서 유사한 명령을 시도하면 sh다음 오류가 발생합니다.

wolf@linux:~$ sh
$ 
$ varA='Aug 01
> Aug 16
> Aug 26'

$ varB='04:25
> 03:39
> 10:06'
$ 
$ echo "$varA $varB"
Aug 01
Aug 16
Aug 26 04:25
03:39
10:06
$ 
$ paste <(printf %s "$varA") <(printf %s "$varB")
sh: 22: Syntax error: "(" unexpected
$ 

비슷한 출력을 얻을 수 있습니까 sh?

답변1

변수의 행 수가 동일한 경우그런 다음 이 pr명령을 사용하여 예를 들어 표준 출력을 두 개의 열로 인쇄할 수 있습니다.

$ printf '%s\n' "$varA" "$varB" | pr -2 -Ts^I       
Aug 01  04:25
Aug 16  03:39
Aug 26  10:06

탭 문자를 나타내는 ^I(명령의 기본 구분 기호와 일치 paste) 키 조합 Ctrl+ 소개를 사용하여 머리글과 바닥글을 V TAB동시에 닫을 수 있습니다.-T

답변2

언제든지 사용할 수 있지만 mktempPOSIX는 이를 지정하지 않으며 이와 관련된 일부 경쟁 조건이 있습니다.

언제든지 다음과 같이 while 루프를 사용할 수 있지만 셸의 루프는 매우 느립니다.

counter=1
while true
do
    left="$(echo "$varA" | awk -v cnt="$counter" 'NR==cnt {printf $0}')"
    right="$(echo "$varB" | awk -v cnt="$counter" 'NR==cnt {printf $0}')"
    if [ -z "$left" ] && [ -z "$right" ]
    then
        break
    fi

    echo "$left $right"
    counter=$((counter + 1))
done

산출:

Aug 01 04:25
Aug 16 03:39
Aug 26 10:06

필요한 경우 더 많은 변수를 추가할 수 있지만 만족스럽지 않습니다. 동적 변수 수에 대해 작동해야 함수동으로 추가해야 하기 때문에 필요합니다. 물론 awk로 모든 것을 구현할 수 있습니다.

답변3

현상금 공지에는 다음과 같은 내용이 나와 있습니다.

대답은 줄 수가 다른 변수에 대한 모든 쉘에서 작동해야 하며 동적 변수 수에 대해 작동해야 합니다.

엄밀히 말하면 "모든 쉘에서 작업"하는 것은 불가능합니다. 거의 할 수 없는 일이에요아무것C 쉘 계열의 쉘과 Bourne 쉘/POSIX 쉘 계열에서 작업하는 것이 매우 중요합니다. 나는 bash, dash(상당히 기본적인 POSIX 호환 셸), ash 및 zsh에서 다음을 테스트했습니다.

여러 줄 변수를 설정하는 몇 가지 방법은 다음과 같습니다.

var17='The
quick
brown
fox'

var42=`echo jumps; echo over; echo -e 'the\nlazy\ndog.'`

another_var=$(printf '%s\n' And they lived happily ever after.)

yet_another=$'The\nend.'

첫 번째 방법(실제로 Enter따옴표 안에 입력)은 전부는 아니더라도 대부분의 POSIX 호환 쉘에서 작동합니다 1 . 두 번째 접근 방식( )은 전부는 아니지만 대부분의 POSIX 호환 쉘 2 에서 작동하지만 ) 부분은 그렇지 않을 수도 있습니다. 일부 오래된 쉘은 인식되지 않을 수 있으며 (완전히?) bashism입니다.`echo word1 ; echo word2`echo -e 'word3\nword4'$(…)$'…'

솔루션이 패턴을 따르는 변수 이름에 의존하지 않는다는 것을 보여주기 위해 의도적으로 일관된 패턴이 없는 변수 이름을 사용합니다. 필요한 경우  varA,  varB,  varC 및  varD (또는 var1,  var2,  var3 및 )를 사용할 수 있습니다.var4

솔루션을 실행하십시오.

$ ./myscript1 "$var17" "$var42" "$another_var" "$yet_another"
The               jumps             And               The
quick             over              they              end.
brown             the               lived
fox               lazy              happily
                  dog.              ever
                                    after.

분명한 사실은 문자열의 줄 수가 다르고 줄의 문자 수가 다르다는 것입니다. 우리는하지 않습니다~ 해야 하다 변수를 사용하면 명령줄에 직접 여러 줄의 문자열 값을 입력할 수 있습니다.

$ ./myscript1 "$var17" "$var42" "$another_var"   $'The\nend.'
The               jumps             And               The
quick             over              they              end.
brown             the               lived
fox               lazy              happily
                  dog.              ever
                                    after.

이것은 myscript1:

#!/bin/dash
first=1
for a in "$@"
do
        if [ "$first" = 1 ]
        then
                set --
                first=
        fi
        file=`mktemp`
        set -- "$@" "$file"
        printf '%s\n' "$a" > "$file"
done
pr -T -m "$@"

rm "$@"

노트:

  • 많은 POSIX 호환 쉘에는 명명된 배열이 없으므로 모두 지원하는 하나의 (이름이 지정되지 않은) 배열인 인수 목록을 사용합니다.
  • for a in "$@"루프에 들어가면 ,그것은값 목록은 쉘의 인수 목록을 변경할 수 있도록 설정됩니다.
  • 첫 번째 단계에서는 손상된 쉘의 매개변수 목록이 사용됩니다 set --.
  • 지나갈 때마다,
    • 임시 파일을 만듭니다. 이식성을 높이기 위해 `mktemp`대신 사용하십시오  $(mktemp)(그렇지 않으면 $(…)일반적으로 선호됨).
    • 매개변수 목록에 파일 이름을 추가합니다.
    • 현재 매개변수를 씁니다(예:원래매개변수 목록)을 파일에 추가합니다. printf이것이 모든 쉘에서 내장 명령으로 사용 가능한지는 모르겠지만 ,쓸 수 있는 (내장 명령 또는 외부 실행 프로그램으로) 박물관 외부의 모든 시스템에 설치됩니다. 시스템에 없으면 printf사용해 볼 수 있지만 echo다음으로 시작하는 문자열에 주의하세요.-또는 포함합니다 \.
  • pr파일 목록에서 호출됩니다.
    • -T페이징을 방지하려면 Steeldriver OK를 사용하십시오 .
    • 파일 병합 용 -m(예: 다중 열 모드)
  • 마지막으로 모든 파일을 삭제합니다.

이는 열이 많은 공백으로 구분된 것처럼 보이지만 항상 그런 것은 아닙니다.

$ ./myscript1 "A very long string about a quick brown fox and a lazy dog" .
A very long string about a quick br .

일어나는 일은 다음과 같습니다.

  • pr파일 수, 즉 열 수를 계산합니다. 그렇게 부르자명사
  • 선 너비를 다음으로 나눕니다.,반올림합니다. (줄 너비는 기본적으로 72입니다.)
  • 그것은 창조한다N열, 각 열 ⌊ 72/N ⌋ 문자 위치가 넓어졌습니다. 이보다 좁은 값은 채워지고, 이보다 넓은 값은 잘립니다.

공백과 잘림을 피하는 것이 좋습니다. pr다음 옵션을 사용하여 선 너비를 지정할 수 있기 때문에 이를 수행할 수 있습니다 -w.

$ ./myscript2 "A very long string about a quick brown fox and a lazy dog" .
A very long string about a quick brown fox and a lazy dog  .

이것은 myscript2:

#!/bin/dash
if [ "$#" = 0 ]
then
        printf '%s\t%s\n' "Usage:" "$0 [string] ..."
        exit 1
fi
first=1
maxwidth=0
for a
do
        if [ "$first" = 1 ]
        then
                set --
                first=
        fi
        file=`mktemp`
        set -- "$@" "$file"
        printf '%s\n' "$a" > "$file"
        width=`printf '%s\n' "$a" | wc -L`
        if [ "$width" -gt "$maxwidth" ]
        then
                maxwidth="$width"
        fi
done
linewidth=`expr "$#" "*" "(" "$maxwidth" + 2 ")"`
pr -T -w "$linewidth" -m "$@"

rm "$@"

노트:

  • for a응 줄임말 for a in "$@". 이는 ash, bash, dash, zsh 및 기타 시스템에서 작동합니다.
  • wc -L입력에서 최대 줄 길이를 찾으십시오. 불행하게도 이것은 GNU 확장입니다. 이는 POSIX 도구를 사용하여 수행할 수 있지만 그렇게 간단하지는 않습니다.
  • 모든 입력에서 가장 긴 줄을 찾기 위해 반복합니다.
  • … 그런 다음 수용할 수 있을 만큼 큰 선 너비를 계산합니다.N 해당 너비의 열에 열 구분을 위한 공백 2개를 더한 것입니다.

____________
1그러나\ 앞에 a를 입력하지  않으면 C 쉘은 이를 허용하지 않습니다 Enter.
2  C 셸에서는 이를 허용하지만 개행 문자를 공백으로 변환합니다. (제가 놓친 부분이 있을 수도 있습니다.)

답변4

1. 명령paste

paste /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF
> $varA
> EOF
> $varB
> EOF

산출:

Aug 01  04:25
Aug 16  03:39
Aug 26  10:06

또는 임시 파일과 함께 붙여넣기를 사용할 수 있지만 프로세스가 오래 걸립니다. temp_file=$(mktemp)


2. 명령pr

printf '%s\n' "$varA" "$varB" | pr -2 -t -s

산출:

Aug 01  04:25
Aug 16  03:39
Aug 26  10:06

관련 정보