MSYS2와 같은 Linux에서 CRLF(캐리지 리턴)를 사용하여 Bash 스크립트를 처리하시나요?

MSYS2와 같은 Linux에서 CRLF(캐리지 리턴)를 사용하여 Bash 스크립트를 처리하시나요?

다음과 같은 간단한 스크립트가 있다고 가정해 보겠습니다 tmp.sh.

echo "testing"
stat .
echo "testing again"

간단하지만 \r\n(즉, CRLF, 즉 캐리지 리턴 + 라인 피드)로 끝납니다. 웹페이지는 줄 끝을 유지하지 않으므로 다음은 16진수 덤프입니다.

$ hexdump -C tmp.sh 
00000000  65 63 68 6f 20 22 74 65  73 74 69 6e 67 22 0d 0a  |echo "testing"..|
00000010  73 74 61 74 20 2e 0d 0a  65 63 68 6f 20 22 74 65  |stat ...echo "te|
00000020  73 74 69 6e 67 20 61 67  61 69 6e 22 0d 0a        |sting again"..|
0000002e

이제 스크립트는 Windows의 MSYS2에서 시작되고 개발되었기 때문에 CRLF 줄 끝이 있습니다. 따라서 Windows 10의 MSYS2에서 실행하면 예상되는 결과를 얻습니다.

$ bash tmp.sh
testing
  File: .
  Size: 0               Blocks: 40         IO Block: 65536  directory
Device: 8e8b98b6h/2391513270d   Inode: 281474976761067  Links: 1
Access: (0755/drwxr-xr-x)  Uid: (197609/      USER)   Gid: (197121/    None)
Access: 2020-04-03 10:42:53.210292000 +0200
Modify: 2020-04-03 10:42:53.210292000 +0200
Change: 2020-04-03 10:42:53.210292000 +0200
 Birth: 2019-02-07 13:22:11.496069300 +0100
testing again

그러나 이 스크립트를 Ubuntu 18.04 시스템에 복사하여 실행하면 다른 결과가 나타납니다.

$ bash tmp.sh
testing
stat: cannot stat '.'$'\r': No such file or directory
testing again

동일한 줄 끝을 가진 다른 스크립트의 Ubuntu bash에서도 이 오류가 발생했습니다.

line 6: $'\r': command not found

...아마도 빈 줄에서 나온 것일 겁니다.

따라서 Ubuntu의 무언가가 Enter에 멈춘 것 같습니다. 나는 보았다BASH 및 캐리지 리턴 동작:

Bash와는 아무 관련이 없습니다. \r 및 \n은 Bash가 아닌 터미널에서 해석됩니다.

...하지만 이것은 명령줄에 그대로 입력된 내용에만 적용되는 것 같습니다. \r및 여기에는 \n이미 스크립트 자체에 입력되어 있으므로 Bash는 이를 여기에서 해석해야 합니다 \r.

Ubuntu의 Bash 버전은 다음과 같습니다.

$ bash --version
GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)

...MSYS2의 Bash 버전은 다음과 같습니다.

$ bash --version
GNU bash, version 4.4.23(2)-release (x86_64-pc-msys)

(그들 사이의 격차는 그다지 크지 않은 것 같습니다 ...)

\r어쨌든 내 질문은 - 우분투/리눅스의 Bash를 "인쇄 가능한 문자"(이 경우 bash의 유효한 명령 부분을 의미함)로 해석하는 대신 이를 무시하도록 설득하는 방법이 있습니까? 이렇게 설명해요)? 편집하다:아니요스크립트 자체를 변환해야 합니다(예를 들어 git에서 이 방법으로 검사하면 CRLF 줄 끝으로 변경되지 않은 상태로 유지됩니다).

EDIT2: 저는 함께 작업 중인 다른 사람이 Windows 텍스트 편집기에서 스크립트를 다시 열고 \r\n스크립트를 다시 도입하고 커밋할 수 있기 때문에 이 방법을 선호합니다. 그러면 끝없는 커밋 스트림이 발생할 수 있습니다. 저장소를 오염시키는 \r\n변환입니다 \n.

편집 2: @Kusalananda가 댓글에서 dos2unix( )를 언급했습니다 sudo apt install dos2unix. 다음과 같이 작성하세요.

$ dos2unix tmp.sh 
dos2unix: converting file tmp.sh to Unix format...

...파일을 내부에서 변환하여 stdout으로 출력합니다. stdin 리디렉션을 설정해야 합니다.

$ dos2unix <tmp.sh | hexdump -C
00000000  65 63 68 6f 20 22 74 65  73 74 69 6e 67 22 0a 73  |echo "testing".s|
00000010  74 61 74 20 2e 0a 65 63  68 6f 20 22 74 65 73 74  |tat ..echo "test|
00000020  69 6e 67 20 61 67 61 69  6e 22 0a                 |ing again".|
0000002b

...그런 다음 원칙적으로 Ubuntu에서 실행할 수 있습니다. 이 경우에는 작동하는 것 같습니다.

$ dos2unix <tmp.sh | bash
testing
  File: .
  Size: 20480       Blocks: 40         IO Block: 4096   directory
Device: 816h/2070d  Inode: 1572865     Links: 27
Access: (1777/drwxrwxrwt)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-04-03 11:11:00.309160050 +0200
Modify: 2020-04-03 11:10:58.349139481 +0200
Change: 2020-04-03 11:10:58.349139481 +0200
 Birth: -
testing again

그러나 기억해야 할 약간 혼란스러운 명령 외에도 stdin은 더 이상 터미널이 아니기 때문에 bash 의미도 변경됩니다. 이 간단한 예에서는 작동할 수 있습니다.https://stackoverflow.com/questions/23257247/pipe-a-script-into-bash더 큰 질문과 같은.

답변1

내가 아는 한 Bash에게 Windows 스타일 줄 끝을 허용하도록 지시할 수 있는 방법은 없습니다.

Windows가 관련된 경우 autocrlf구성 플래그를 사용하여 커밋 시 줄 끝을 자동으로 변환하는 Git의 기능에 의존하는 것이 일반적인 관행입니다. 예시 보기줄 끝 부분에 대한 GitHub 문서, 이는 GitHub에만 국한되지 않습니다. 이런 방식으로 파일은 저장소에서 Unix 스타일 줄 끝으로 커밋되고 각 클라이언트 플랫폼에 맞게 변환됩니다.

(그 반대는 문제가 되지 않습니다. MSYS2는 Windows에서 Unix 스타일 줄 끝을 잘 처리합니다.)

답변2

당신은 사용해야합니다binfmt_misc이런 이유로[1].

먼저 로 시작하는 파일을 처리하는 매직을 정의한 #! /bin/bash<CR><LF>다음 실행 가능한 인터프리터를 만듭니다. 인터프리터는 다른 스크립트일 수 있습니다.

INTERP=/path/to/bash-crlf

echo ",bash-crlf,M,,#! /bin/bash\x0d\x0a,,$INTERP," > /proc/sys/fs/binfmt_misc/register
cat > "$INTERP" <<'EOT'; chmod 755 "$INTERP"
#! /bin/bash
script=$1; shift; exec bash <(sed 's/\r$//' "$script") "$@"
EOT

테스트를 받아보세요:

$ printf '%s\r\n' '#! /bin/bash' pwd >/tmp/foo; chmod 755 /tmp/foo
$ cat -v /tmp/foo
#! /bin/bash^M
pwd^M
$ /tmp/foo
/tmp

예제 인터프리터에는 두 가지 문제가 있습니다.1.검색할 수 없는 파일(파이프라인)을 통해 스크립트를 전달하기 때문에 bash는 스크립트를 바이트 단위로 읽습니다. 이는 매우 비효율적입니다.2.모든 오류 메시지에는 /dev/fd/63원본 스크립트 이름 대신 인용문이나 유사한 이름이 표시됩니다.

[1] 물론 /bin/bash^Mbinfmt_misc를 사용하는 대신 OpenBSD와 같은 다른 시스템에서도 작동하는 인터프리터에 대한 심볼릭 링크를 만들 수 있습니다.

ln -s /path/to/bash-crlf $'/bin/bash\r'

그러나 Linux에서 shebanged 실행 파일은 binfmt_misc에 비해 이점을 제공하지 않으며 시스템 디렉토리에 쓰레기를 넣는 것은 올바른 전략이 아니며 시스템 관리자가 고개를 흔들게 될 것입니다 ;-)

답변3

알겠습니다. 다음을 통해 몇 가지 해결 방법을 찾았습니다.

"가입" 심볼릭 링크

최신 UNIX 시스템에는 저장 방법에 관계없이 임의의 데이터를 파일로 표시하는 방법이 있습니다.퓨즈. FUSE를 사용하면 파일에 대한 모든 작업(생성, 열기, 읽기, 쓰기, 디렉터리 나열 등)이 프로그램의 일부 코드를 호출하고 해당 코드는 원하는 모든 작업을 수행할 수 있습니다. 바라보다실제로 명령인 더미 파일을 만듭니다.. 시도해 볼 수 있습니다스크립트 파일 시스템또는퓨즈, 또는 야심이 있다면 스스로 할 수 있습니다.

... 그리고실제로 명령인 더미 파일을 만듭니다.

당신은 찾고있을 수 있습니다명명된 파이프.

따라서 접근 방식은 명명된 파이프를 만들고 dos2unix출력한 다음 bash명명된 파이프를 호출하는 것입니다.

tmp.sh여기에는 다음과 같이 끝나는 원래 CRLF 줄이 있습니다 /tmp. 먼저 명명된 파이프를 만들어 보겠습니다.

tmp$ mkfifo ftmp.sh

이제 다음 명령을 실행하면:

tmp$ dos2unix <tmp.sh >ftmp.sh

...그렇다면 다음과 같이 말해보세요.

~$ cat /tmp/ftmp.sh | hexdump -C
00000000  65 63 68 6f 20 22 74 65  73 74 69 6e 67 22 0a 73  |echo "testing".s|
00000010  74 61 74 20 2e 0a 65 63  68 6f 20 22 74 65 73 74  |tat ..echo "test|
00000020  69 6e 67 20 61 67 61 69  6e 22 0a                 |ing again".|
0000002b

...변환이 완료되었음을 알 수 있습니다. 명령 cat실행이 완료된 후 dos2unix <tmp.sh >ftmp.sh이전에 차단된 명령이 종료되었습니다.

dos2unix따라서 "무한" while 루프에서 명명된 파이프에 쓰기를 설정할 수 있습니다.

tmp$ while [ 1 ] ; do dos2unix <tmp.sh >ftmp.sh ; done

..."긴밀한" 루프이더라도 대부분의 경우 while 루프 내부의 명령이 차단되므로 문제가 되지 않습니다.

그러면 다음과 같이 할 수 있습니다.

~$ bash /tmp/ftmp.sh
testing
  File: .
  Size: 4096        Blocks: 8          IO Block: 4096   directory
Device: 801h/2049d  Inode: 5276132     Links: 7
...
testing again
$

...스크립트가 제대로 작동하는 것 같습니다.

이 접근 방식의 장점은 tmp.sh텍스트 편집기에서 원본 파일을 열 수 있다는 것입니다. CRLF로 끝나는 새 코드를 작성한 다음 저장 tmp.sh하면 Linux에서 bash /tmp/ftmp.sh저장된 최신 버전이 실행됩니다.

이것의 문제는 read -p "Enter user: " user실제 터미널 stdin에 의존하는 이와 같은 명령이 실패하거나 오히려 실패하지 않는다는 것입니다. 그러나 시도하면 다음과 같습니다./tmp/tmp.sh

echo "testing"
stat .
echo "testing again"
read -p "Enter user: " user
echo "user is: $user"

...그러면 다음과 같이 출력됩니다.

$ bash /tmp/ftmp.sh
testing
  File: .
  Size: 4096        Blocks: 8          IO Block: 4096   directory
...
 Birth: -
testing again
Enter user: tyutyu
user is: tyutyu
testing
  File: .
  Size: 4096        Blocks: 8          IO Block: 4096   directory
...
 Birth: -
testing again
Enter user: asd
user is: asd
testing
...

...등등 - 즉, 터미널에 있는 키보드의 표준 입력이 올바르게 해석되지만 어떤 이유로 스크립트가 반복을 시작하고 처음부터 계속해서 다시 실행됩니다(우리가 그렇지 않으면 이런 일이 발생하지 않습니다) read -p ...)original ) 명령을 사용하세요 tmp.sh. 아마도 일부 리디렉션 항목이 있을 수 있습니다( 루프 명령에 무언가를 추가하는 것과 같은 0>1&while.sh ; 실제로 이와 같이 루핑을 시작하는 스크립트가 있었는데 wget스크립트 끝에 명시적을 추가하는 것만으로도 스크립트 루핑을 중지하는 것처럼 보였습니다). 또한 가능합니다. - 하지만 지금까지 제가 사용해야 하는 스크립트에는 비슷한 명령이 없으므로 이 접근 방식이 저에게 적합할 수 있습니다.exit.shread -p

답변4

Bash 스크립트의 각 줄 끝에 파운드 기호(#)를 삽입할 수 있습니다. 이런 식으로 Unix의 쉘은 CR을 주석으로 처리하고 신경 쓰지 않습니다.

"hex", 모든 줄은 다음으로 끝나야 합니다.

0x23 0x0D 0x0A

예:

echo "testing" #
stat . #
echo "testing again" #

관련 정보