我在 Linux 上使用 GNU Make。這是我的 Makefile。
foo:
printf '\x41\n'
bar:
printf '\x41\n' | cat
這是輸出:
$ make foo
printf '\x41\n'
A
$ make bar
printf '\x41\n' | cat
\x41
使用的外殼是 Dash:
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Sep 12 04:41 /bin/sh -> dash
為什么當我通過管道時輸出會變得不同cat?
奇怪的是,dash當我直接在其上執行命令時,它本身具有不同的行為。
$ printf '\x41\n'
\x41
$ printf '\x41\n' | cat
\x41
這里發生了什么?為什么 Makefile 中的命令輸出與 Dash 中的輸出不一致?
uj5u.com熱心網友回復:
在foo情況下,運行外部 printf 可執行檔案而不是內置 shell(請參閱此答案 https://unix.stackexchange.com/questions/202302/variable-definition-in-bash-using-the-local-keyword/202326#202326 這解釋了 shell 內置和外部可執行檔案之間的區別),正如您在 strace 中看到的那樣::
$ strace -f make
(...)
[pid 1396] execve("/usr/bin/printf", ["printf", "\\x41\\n"], 0x56453618b880 /* 14 vars */ <unfinished ...>
因此,即使使用了破折號,也會列印 A,因為作為 coreutils 一部分的 printf 理解 \x 序列:
$ dash
$ /usr/bin/printf '\x41\n'
A
如您在 strace 中看到的,在bar情況下 make 運行 dash builtin:
$ strace -f make bar
(...)
[pid 1414] execve("/bin/sh", ["/bin/sh", "-c", "printf '\\x41\\n' | cat"], 0x55835836a8e0 /* 14 vars */ <unfinished ...>
uj5u.com熱心網友回復:
您看到不同行為的原因是\xNNPOSIX 標準中沒有為printf. 該標準僅定義以八進制指定的特殊字符的行為,使用\oNNN. 它不支持十六進制形式。
看到不同行為的原因是在簡單的情況下:
foo:
printf '\x41\n'
make不需要呼叫shell:它可以直接運行呼叫/usr/bin/printf系統上程式的命令。除了 POSIX 所需的功能外,該程式還具有額外的功能,并且它可以處理\xNN字符。
在更復雜的情況下:
bar:
printf '\x41\n' | cat
這需要make呼叫 shell,因為make無法處理管道等。當你呼叫 shell 時,它使用的是 shell 的內置而printf不是單獨的程式/usr/bin/printf。由于您的外殼是dash,它只提供符合 POSIX 的行為(大部分情況下),它的printf內置不處理非標準\xNN字符。
簡短的回答:堅持 POSIX 定義的行為,它將一直有效。
uj5u.com熱心網友回復:
讓我們使用一行陳述句來總結其他答案的結果,其中輸出提供了看到這一點所需的所有證據:
- 堅持字符的八進制表示不存在使用和不使用管道獲得不同結果的問題
cat makeprintf確實在不使用管道的情況下運行系統,并且使用管道printf的外殼程式是什么是您看到不同行為的核心原因,旁邊是 POSIX 標準僅定義以八進制指定的特殊字符的行為,使用(但不不支持)printfcatprintf\NNN\xNN
在提供的命令列的幫助下,任何人現在都可以嘗試在自己的系統上重現該問題,其副作用是查看 make 使用了哪個 shell:
$貓制作檔案;回聲-----------; strace -o strace.out -f 制作;回聲-----------; 貓 strace.out | grep 'printf' | grep -v 'ENOENT'
(不要忘記將輸出的第一部分存盤在"Makefile"之前呼叫的檔案中):
$ cat Makefile; echo -----------; strace -o strace.out -f make; echo -----------; cat strace.out | grep 'printf' | grep -v 'ENOENT'
all: pXA pXAcat poA poAcat
pXA:
printf '\x41\n'
pXAcat:
printf '\x41\n' | cat
poA:
printf '\101\n'
poAcat:
printf '\101\n' | cat
-----------
printf '\x41\n'
A
printf '\x41\n' | cat
\x41
printf '\101\n'
A
printf '\101\n' | cat
A
-----------
28434 write(1, "printf '\\x41\\n'\n", 16) = 16
28435 execve("/usr/bin/printf", ["printf", "\\x41\\n"], [/* 69 vars */]) = 0
28434 write(1, "printf '\\x41\\n' | cat\n", 22) = 22
28436 execve("/bin/sh", ["/bin/sh", "-c", "printf '\\x41\\n' | cat"], [/* 69 vars */]) = 0
28434 write(1, "printf '\\101\\n'\n", 16) = 16
28439 execve("/usr/bin/printf", ["printf", "\\101\\n"], [/* 69 vars */]) = 0
28434 write(1, "printf '\\101\\n' | cat\n", 22) = 22
28440 execve("/bin/sh", ["/bin/sh", "-c", "printf '\\101\\n' | cat"], [/* 69 vars */]) = 0
順便說一句:如果您查看"strace.out"檔案的第一行,您可以看到哪個 make 正在處理 Makefile:
28434 execve("/usr/bin/make", ["make"], [/* 64 vars */]) = 0
"Makefile"在運行長命令列之前要保存的代碼下方:
all: pXA pXAcat poA poAcat
pXA:
printf '\x41\n'
pXAcat:
printf '\x41\n' | cat
poA:
printf '\101\n'
poAcat:
printf '\101\n' | cat
如果您make沒有給出與上面相同的結果,請將其調整為make all以獲得所有輸出。
PS 請注意,帶有printfin 的行的縮進"Makefile"必須使用制表符而不是空格來完成(因此用制表符替換空格或從編輯而不是查看文本框中復制腳本)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/532996.html
標籤:linux壳生成文件逃跑
上一篇:用jq格式化視窗值
