一般化する
xargs -I s printf s
xargs -n 1 printf
?
背景
0x00を含むことができるバイナリデータを処理します。次のように、バイナリデータをテキストに変換する方法を知っています。
# make sure that you have done this: export LC_ALL=C
od -A n -t x1 -v | # or -t o1 or -t u1 or whatever
tr ABCDEF abcdef | # because POSIX doesn't specify in which case
tr -d ' \t\n' | # because POSIX says they are delimiters
fold -w 2 |
grep . # to make sure to terminate final line with LF
...バイナリに戻す方法は次のとおりです。
# input: for each line, /^[0-9a-f]\{2\}$/
# also make sure export LC_ALL=C before
awk -v _maxlen="$(getconf ARG_MAX 2>/dev/null)" '
BEGIN{
# (1) make a table
# assume that every non-null byte can be converted easily
# actually not portable in Termux; LC_ALL=C does not work and
# awk is gawk by default, which depends on locale.
# to deal with it, here is alternative:
# for(i=0;i<256;i++){
# xc[sprintf("%02x",i)]=sprintf("\\\\%03o",i);
# xl[sprintf("%02x",i)]=5;
# }
# # and skip to (2)
# but why not just env -i awk to force one true awk, if so.
# also is not it pretty rare that C locale is not available?
for(i=1;i<256;i++){
xc[sprintf("%02x",i)]=sprintf("%c",i);
xl[sprintf("%02x",i)]=1;
}
# now for chars that requires special converting.
# numbers; for previous char is \\ooo.
for(i=48;i<58;i++){
xc[sprintf("%02x",i)]=sprintf("\\\\%03o",i);
xl[sprintf("%02x",i)]=5;
}
# and what cannot be easily passed to xargs -n 1 printf
# null
xc["00"]="\\\\000"; xl["00"]=5;
# <space>
xc["09"]="\\\\t"; xl["09"]=3;
xc["0a"]="\\\\n"; xl["0a"]=3;
xc["0b"]="\\\\v"; xl["0b"]=3;
xc["0c"]="\\\\f"; xl["0c"]=3;
xc["0d"]="\\\\r"; xl["0d"]=3;
xc["20"]="\\\\040"; xl["20"]=5;
# meta chars for printf
xc["25"]="%%"; xl["25"]=2;
xc["5c"]="\\\\\\\\";xl["5c"]=4;
# hyphen; to prevent to be treated as if it were an option
xc["2d"]="\\\\055"; xl["2d"]=5;
# chars for quotation
xc["22"]="\\\""; xl["22"]=2;
xc["27"]="\\'\''"; xl["27"]=2;
# (2) preparation
# reason why 4096: _POSIX_ARG_MAX
# reason why length("printf "): because of ARG_MAX
# reason why 4096/2 and _maxlen/2: because some xargs such as GNU specifies buffer length less than ARG_MAX
if(_maxlen==""){
maxlen=(4096/2)-length("printf ");
}else{
maxlen=int(_maxlen/2)-length("printf ");
}
ORS=""; LF=sprintf("\n");
arglen=0;
}
{
# (3) actual conversion here.
# XXX. not sure why arglen+4>maxlen.
# but I think maximum value for xl[$0] is 5.
# and maybe final LF is 1.
if(arglen+4>maxlen){
print LF;
arglen=0;
}
print xc[$0];
arglen+=xl[$0];
}
END{
# for some xargs who hates input w/o LF termination
if(NR>0)print LF;
}
' |
xargs -n 1 printf
空の入力に関する問題が見つかりました。 GNU/Linux では、次のように失敗しました。
$ xargs -n 1 printf </dev/null
printf: missing operand
Try 'printf --help' for more information.
その後、ブロックをxargs -n 1 printf 2>/dev/null || :
追加して選択肢を見つけました。 ShellShoccar-jpnプログラムで実際に使用されているのは初めてですが、少し強力なようです。 2番目は最後のものほどきれいではありません。 3番目のアプローチは、GNU / Linuxだけでなく、すべての(または他のほとんどの)環境に代わるものですか?私はGNU / Linuxだけを持っているので、他の環境では私のアイデアを検証する方法がわかりません。最も簡単な方法は、ソースを入手して参照するか、マニュアルを参照することです。まったく確認できない場合はあきらめなければなりません。if(NR==0)printf"\"\"\n";
END
xargs -I s printf s
私の知識
printf
POSIXが言ったように、ここには少なくとも1つの引数が必要なようです。- 一部は
xargs
LF出口のない入力を無視します。grep ^ | xargs something here
LF出口のない入力よりも移植性が優れています。xargs something here
- xargsは、空でない行がなければ入力用に移植可能ではありません。
printf ' \n\n' | xargs echo foo
FreeBSDとGNU / Linuxfoo
では何も出力されません。この場合、xargsコマンドがその入力に対して安全であることを確認するか、コマンドがエラーを無視するようにする必要があります。 - FreeBSDのxargsは同じ引数を受け取り、
$@
GNU / Linuxの引数は同じように受け取ります"$@"
。 - バックスラッシュを介したエスケープは、出力を
printf '\\\\\\'"'" | sed "$(printf 's/[\047\042\\]/\\\\&/g')" | xargs printf
取得するのと同じようにxargsで動作します。\'
ポリスチレン
xargs -E ''
特定のxargsのデフォルトのため、オプションがないよりもこれがより互換性があると思います-E _
。
ベストアンサー1
xargs
移植性(およびインタフェース設計)の面では、おそらく最悪のPOSIXユーティリティです。私はそれを遠ざけるでしょう。どうですか?
<file.hex awk -v q="'" -v ORS= '
BEGIN{
for (i=0; i<256; i++) c[sprintf("%02x", i)] = sprintf("\\%o", i)
}
NR % 50 == 1 {print sep"printf "q; sep = q"\n"}
{print c[$0]}
END {if (sep) print q"\n"}
' | sh
例えば?
このawk
セクションの出力は次のようになります。
printf '\61\12\62\12\63\12\64\12\65\12\66\12\67\12\70\12\71\12\61\60\12\61\61\12\61\62\12\61\63\12\61\64\12\61\65\12\61\66\12\61\67\12\61\70\12\61\71\12\62\60'
printf '\12\62\61\12\62\62\12\62\63\12\62\64\12\62\65\12\62\66\12\62\67\12\62\70\12\62\71\12\63\60\12\63\61\12\63\62\12\63\63\12\63\64\12\63\65\12\63\66\12\63'
printf '\67\12\63\70\12\63\71\12\64\60\12\64\61\12\64\62\12\64\63\12\64\64\12\64\65\12\64\66\12\64\67\12\64\70\12\64\71\12\65\60\12\65\61\12\65\62\12\65\63\12'
printf '\65\64\12\65\65\12\65\66\12\65\67\12\65\70\12\65\71\12\66\60\12\66\61\12\66\62\12\66\63\12\66\64\12\66\65\12\66\66\12\66\67\12\66\70\12\66\71\12\67\60'
printf '\12\67\61\12\67\62\12\67\63\12\67\64\12\67\65\12\67\66\12\67\67\12\67\70\12\67\71\12\70\60\12\70\61\12\70\62\12\70\63\12\70\64\12\70\65\12\70\66\12\70'
printf '\67\12\70\70\12\70\71\12\71\60\12\71\61\12\71\62\12\71\63\12\71\64\12\71\65\12\71\66\12\71\67\12\71\70\12\71\71\12\61\60\60\12'
説明するsh
。組み込みsh
の実装では、追加のprintf
プロセスは分岐しません。そうでない場合は、ARG_MAXの制限を避けるのに十分な行が短くなければなりませんが、printf
それでも50バイトあたり1行以下で実行する必要があります。
ARG_MAX値だけでは、実際にコマンドラインの最大長を決定することはできません。この制限に達して処理する方法は、システムとそのバージョンによって大きく異なります。多くの場合、ARG_MAXはポインタargv[]
とリストの累積サイズenvp[]
(通常は64ビットシステムでは引数/envvarあたり8バイト)とバイト単位の各引数/環境文字列サイズ(NUL区切り文字を含む)に制限されます。 Linuxには、個々のパラメータのサイズに対する独立した制限もあります。
\12
たとえば、交換は\n
ASCIIベースのシステムでのみ機能します。 POSIX は NUL 以外の文字エンコーディングを指定しません。 ASCIIの代わりにEBCDICのいくつかのバリエーションを使用するPOSIXシステムがまだあります。