web-dev-qa-db-ja.com

シェルスクリプトのprintfは\ x%xを実行できません

ここで、ECHO-VAR\xFF\xFF\xFF\x00$fb_COLOR15)を生成します。これらはコマンドラインで機能します。

CP="`ECHO-VAR`" printf $CP | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0
printf "`ECHO-VAR`" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0
printf "${fb_COLOR15}" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0

ただし、シェルスクリプト(#!/bin/sh)では機能しません。 \xFF\xFF\xFF\x00を介してパイプされた場合でも、4文字ではなくxFFxFFxFFx00またはsed 's/\\/\\\\\\\\\\/g'のみを出力します。

これらは機能しますが、bash: printf: missing hex digit for \x(x4)を使用すると、最終的に期待するものが生成されます。

printf `printf "\x%x\x%x\x%x\x%x" 255 255 255 0` | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0
printf "`printf "\\x%x\\x%x\\x%x\\x%x" 255 255 255 0`" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0
printf "`printf "\\\x%x\\\x%x\\\x%x\\\x%x" 255 255 255 0`" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0
printf "`printf "\\\\x%x\\\\x%x\\\\x%x\\\\x%x" 255 255 255 0`" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0

ただし、これは機能します(5つのスラッシュ):

printf "`printf "\\\\\x%x\\\\\x%x\\\\\x%x\\\\\x%x" 255 255 255 0`" | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0

ただし、シェルスクリプト(#!/bin/sh)では、同じエラーが発生します。

入力形式を\ x型から10進数の4つの引数形式に変更する前に、 `実行、階層化エコー、およびprintfのさまざまな組み合わせを5時間試しましたが、それでも失敗しました。


参考:32ビットフレームバッファ上に白いドットが生成されます。

5分で、コマンドラインとシェルスクリプトの両方から次のコマンドを使用して動作するようになりました。

bas d2a.bas 255 255 255 0 | dd status=none bs=4 count=$(( ( 1360 * 100 ) + 100 )) > /dev/fb0

d2a.bas

 1 rem D2A.BAS - decimal arguments to ASCII characters
 10 for i=1 to 255
 20   a$=command$(i)
 30   if a$="" then
 40     i=255
 50   else
 60     a=val(A$)
 70     print chr$(a);
 80   end if
 90 next

シェルが抽象化レベルごとにエスケープされた\文字の1つのレイヤーを処理し、コマンドまたはバイナリ(/usr/bin/printfもテストしました)がエスケープされた\文字の別のレイヤーを解析することを理解しています。

しかし、シェルスクリプトでどちらの形式も機能しない理由がわかりません。シェルスクリプトは、出力文字列に存在するすべての文字に\文字を追加するだけで機能するはずです。

誰かが何が起こっているのか知っていますか、それともこれは実際のバグですか?

これはSHモードのBASHのバグだと思います。以下の投稿を参照するか、面倒な作業を省いてBASスクリプトを使用してください。

2
Paul Wratt

\はそこで数回使用されています:

  • コマンド置換の`...`形式の場合。 $(...)を使用するのが最善です。
  • $`\などの文字を二重引用符で囲んでエスケープします。代わりに一重引用符を使用することをお勧めします。
  • printfのフォーマット引数で\をエスケープします
  • その\xHHシーケンスを他のprintfのformat引数に導入します(ただし、標準ではありません)。

したがって、次のいずれかになります。

printf `printf "\\\\\\\\x%x" 255 255 255 0`
printf `printf '\\\\x%x' 255 255 255 0`
printf $(printf '\\x%x' 255 255 255 0)

つまり、\\を出力するには、\を右端のprintfに渡す必要がありますが、`...`を使用する場合は、各\\でエスケープし、"..."に対して再度実行する必要があります。

それでも、ここでは不要なsplit + glob演算子を呼び出しています。そう:

printf "$(printf '\\x%x' 255 255 255 0)"

または移植可能:

printf "$(printf '\\%o' 255 255 255 0)"

いくつかのawk実装の場合(すべてが0で動作するわけではありません):

LC_ALL=C awk 'BEGIN{printf "%c%c%c%c", 255, 255, 255, 0}'

Perlの場合:

Perl -e 'print pack "C*", @ARGV' 255 255 255 0

zshサブシェルのフォークを回避する代替手段:

(){setopt localoptions nomultibyte; printf %s ${(#)@}} 255 255 255 0

サブシェルのフォークを回避するbash代替(最近のバージョンのzshでも機能します):

printf -v x '\\%o' 255 255 255 0
printf "$x"
2

もう一度同じ問題が発生しましたが、今回は入力データをリファクタリングしてdecimalにすることができませんでした。

他の誰かが別の方法で表示できない限り、SHモードのときのBASHの内部printfcannot manページとヘルプページの内容に関係なく、「\ xFF」文字を出力します。少なくともBASHでは、これはバグだと思います。

#!/bin/sh
printf "\x46\x47"

文字列から引用符を削除すると、シェルはx部分を処理せずに引用符をエスケープします。

次に何が機能しますか:

#!/bin/sh
PF=`which printf`
$PF "\x46\x47"

したがって、現在のソリューションは、hexadecimalastextで、文字またはが必要ですbyte、各(テキスト)バイトをスクリプトファイルにパイプアウトし(2番目のコードが示すように)、各値の前に\xを追加し、スクリプトをchmodスクリプトの内部から、次の行でスクリプトを実行し、必要に応じてスクリプトを削除します。

0
Paul Wratt