일반화하다

일반화하다

일반화하다

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 프로그램에서 실제로 사용되는 것은 처음 봤지만 좀 강력한 것 같아요. 두 번째 것은 마지막 것만큼 깨끗하지 않습니다. 세 번째 접근 방식이 GNU/Linux뿐만 아니라 모든(또는 대부분의 다른) 환경에 대한 대안이 될 수 있습니까? 저는 GNU/Linux만 가지고 있어서 다른 환경에서는 제 아이디어를 검증하는 방법을 모르겠습니다. 가장 쉬운 방법은 소스를 구하여 참조하거나 매뉴얼을 참조하는 것입니다. 전혀 확인할 수 없다면 포기해야 합니다.if(NR==0)printf"\"\"\n";ENDxargs -I s printf s

내 지식

  • printfPOSIX가 말했듯이 여기에는 최소한 하나의 인수가 필요한 것 같습니다.
  • 일부는 xargsLF 종료가 없는 입력을 무시합니다. grep ^ | xargs something hereLF 종료가 없는 입력보다 이식성이 더 좋습니다.xargs something here
  • xargs는 비어 있지 않은 줄이 없으면 입력용으로 이식 가능하지 않습니다. printf ' \n\n' | xargs echo fooFreeBSD 및 GNU/Linux foo에서는 아무것도 출력되지 않습니다 . 이 경우 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예 를 들어 교체는 \nASCII 기반 시스템에서만 작동합니다. POSIX는 NUL 이외의 문자 인코딩을 지정하지 않습니다. ASCII 대신 EBCDIC의 일부 변형을 사용하는 POSIX 시스템이 여전히 있습니다.

관련 정보