awk를 사용하여 모든 특수 문자를 분할하고 이스케이프하는 방법

awk를 사용하여 모든 특수 문자를 분할하고 이스케이프하는 방법

분할을 사용하여 char 배열을 만들려고하는데 지금까지는 작동합니다.

문제는 입력 문자열의 문자 앞에 \가 있을 때입니다. \는 다음 문자를 이스케이프하고 손실되어 배열에서 고려되지 않기 때문에 문자로 간주되지 않습니다.

목표는 나중에 사용할 수 있도록 모든 것을 charArray에 저장하는 것입니다.

function getLineChars {
   l=1
   for line in ${fileLinesArray[@]}; do
      charArray=$(echo | awk -v str="${line}" '{
         split(str, lineChars, "")
         for (i=1; i<=length(str); i++) {
            printf("%s ", lineChars[i])
         }
      }')
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

따라서 가장 중요한 것은 이 경우를 제외하고 모든 특수 문자나 이상한 문자를 배열로 인쇄하는 것입니다.

3\zKhj awk: warning: escape sequence `\z' treated as plain `z'

배열 결과는 다음과 같습니다.

3 z K h j

\ 문자가 누락되어 배열에 포함되어야 합니다.

이것에 대해 또 무엇을 할 수 있습니까? awk를 사용해봐도 괜찮습니까? 아니면 다른 것을 제안하시겠습니까?

미리 감사드립니다.

답변1

꼭 사용해야 한다면 여기에 문자열을 awk입력하세요 .${line}

function getLineChars {
   l=1
   for line in "${fileLinesArray[@]}"; do
      charArray=$( awk '{ split($0, lineChars, "")
                          for (i=1; i<=length($0); i++) {
                              printf("%s ", lineChars[i])
                          }
                        }' <<< "${line}" )
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

시험 운전해 보세요:

$ fileLinesArray=( '3\zKhj' )
$ getLineChars
3 \ z K h j

그런데 안에는 무엇이 들어있나요 charArray[@]?

$ typeset -p charArray
declare -- charArray="3 \\ z K h j "

실제로는 후행 공백이 있는 문자열입니다.

문자 배열을 정말로 원한다면 ; charArray=$( awk ... )로 바꾸세요 charArray=( $( awk ... ) ). 변경하고 테스트해 보세요.

$ getLineChars                                                                           
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

이제 실제 문자 배열이 생겼습니다.


다음과 같이 더 간단한 것을 선택할 수도 있습니다.

function getLineChars {
   l=1
   for line in "${fileLinesArray[@]}"; do
      mapfile -t charArray < <( grep -o . <<< "${line}" )
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

노트:사용하도록 업데이트되었습니다 mapfile(동의어 readarray, Ed Morton에게 감사드립니다).

시험 운전해 보세요:

$ getLineChars
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

또는 $( grep ... )정규식과 배열을 통해 BASH_REMATCH[]하위 프로세스 호출을 제거 할 수 있습니다.

getLineChars() {
    l=1
    for line in "${fileLinesArray[@]}"; do 
        [[ "${line}" =~ ${line//?/(.)} ]] && charArray=( "${BASH_REMATCH[@]:1}" )
        l=$(($l+1))
        echo "${charArray[@]}" 
    done
}

어디:

  • ${line//?/(.)}- 각 문자를 리터럴 문자열로 대체하여 (.)각 문자에 대한 캡처 그룹을 제공합니다(참고: do아니요큰따옴표로 묶어주세요)
  • "${BASH_REMATCH[@]:1}"- 인덱스 == 1로 시작하고 배열 끝까지 가는 모든 배열 항목을 가져옵니다.

시험 운전해 보세요:

$ getLineChars
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

$ typeset -p BASH_REMATCH
declare -a BASH_REMATCH=([0]="3\\zKhj" [1]="3" [2]="\\" [3]="z" [4]="K" [5]="h" [6]="j")

답변2

빈 FS를 분할하는 split(str, lineChars,"")것은 정의되지 않은 동작이므로 다른 awks에서 다른 작업을 수행합니다. -v변수 값을 awk에 전달하면 의도적으로 이스케이프 시퀀스가 ​​확장됩니다. 이는 원하는 것이 아닙니다(참조awk 스크립트에서 쉘 변수를 사용하는 방법대안으로) 에코와 파이프를 사용하면 불필요한 오버헤드와 취약성이 발생합니다(사용하는 문자 및 에코 버전에 따라 중단됨).

charArray코드에서:

charArray=$(echo | awk '...')

배열이 아니라 스칼라입니다. 다음을 의미하는 것 같습니다.

charArray=( $(echo | awk '...') )

그러나 명령 출력으로 배열을 채우면 array=( command )명령 출력이 글로빙 및 파일 이름 확장을 위해 셸에 노출되므로 어떤 명령으로도 이 작업을 수행하지 말고 readarray대신 다음 두 가지를 사용해 보십시오.

$ line='a*b c'; array=( $(grep -o . <<<"$line") )
declare -p array
<output will not include the `*` or blank char from `$line` but will include the names of all files in your current directory>

$ line='a*b c'; readarray -t array < <(grep -o . <<<"$line")
$ declare -p array
declare -a array=([0]="a" [1]="*" [2]="b" [3]=" " [4]="c")

bash따라서 견고성과 이식성을 위해 awk를 호출하는 쉘 루프를 사용하여 이 작업을 수행하려면(쉘을 사용한다고 가정) 다음을 수행하십시오.

$ line='3\zK*h jÃk'

$ readarray -t charArray < <(
    awk '
        BEGIN {
            line = ARGV[1]
            ARGV[1] = ""
            lgth = length(line)
            for (i=1; i<=lgth; i++) {
                print substr(line,i,1)
            }
        }
    ' "$line"
)

$ declare -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="*" [5]="h" [6]=" " [7]="j" [8]="Ã" [9]="k")

그러나 쉘 루프가 한 번에 한 줄씩 awk를 호출하도록 하는 것보다 수행하려는 모든 작업을 수행하는 더 나은 방법이 거의 확실하므로 더 큰 문제에 대한 도움이 필요하면 샘플 입력/출력과 함께 새 질문을 게시하십시오.

아, 그리고변수 이름을 지정하지 마세요.l숫자와 너무 비슷해 1코드가 혼동되고 함수에 다른 문제가 있으므로 복사하여 붙여넣으세요.http://shellcheck.net알려드리고 해결하도록 도와드리겠습니다.

답변3

awk의 코드 문자열에 값을 추가하여 변수를 awk로 전송하려는 경우:

awk 'BEGIN {var="'"$BASH_variable"'"}

내 라이브러리에서 이 기능을 사용할 수 있습니다.


declare g_RV  #-- g_RV ... global return value

#-- call:        g_serialize_STR_ForAWK  [string to serialize STR] [option bINT]
#-- description: converts a string to combine it with an awk variable declaration: 'BEGIN { var="'[serialized string STR]'" ..}'
#--              '\' becomes '\\', '"' becomes '\"', $'\n' becomes '\n' 
#-- parameters:  $1 ... string to serialize STR - a string you want to transmit to awk per variable declaration (var="...")
#--              $2 ... option bINT optional - convert it with bash (0), convert it with sed (1), Standard (0)
#-- returnValue: written to g_RV - the converted string STR
#-- depends on:  variables - g_RV
function g_serialize_STR_ForAWK ()
    {
    local -i option=$2
    
    #-- use sed for converting
    if ((option)); then
        g_RV=$(sed -z 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g' <<< $1";")    
        g_RV=${g_RV:0:-1}
    #-- use bash for converting    
    else
        g_RV=${1//'\'/'\\'}; g_RV=${g_RV//'"'/'\"'}; g_RV=${g_RV//$'\n'/'\n'}
    fi
    }

답변4

사용진주그리고/또는행복하다백슬래시 이스케이프 문자를 그대로 유지

  • 진주해결책:
~$ echo -n '3\zKh j' | perl -ne 'print split /(?<!\\)/'
3\zKh j

#visualize split with Data::Dumper module

~$ ~$ echo -n '3\zKh j' | perl -MData::Dumper -ne 'print Dumper split /(?<!\\)/'
$VAR1 = '3';
$VAR2 = '\\z';
$VAR3 = 'K';
$VAR4 = 'h';
$VAR5 = ' ';
$VAR6 = 'j';

#and also Unicode (add `-CSDA` to command line)

~$ echo -n '3\zKh jÃkΣ' | perl -CSDA -MData::Dumper -ne 'print Dumper split /(?<!\\)/'
$VAR1 = '3';
$VAR2 = '\\z';
$VAR3 = 'K';
$VAR4 = 'h';
$VAR5 = ' ';
$VAR6 = 'j';
$VAR7 = "\x{c3}";
$VAR8 = 'k';
$VAR9 = "\x{3a3}";

  • 행복하다(이 언어는 이전에는 Perl6으로 알려졌습니다.) 해결책:
~$ echo -n '3\zKh j' | raku -ne '.comb(/ \\? . /).print'
3 \z K h   j

#visualize split with `raku` built-in

~$ echo -n '3\zKh j' | raku -ne '.comb(/ \\? . /).raku.print'
("3", "\\z", "K", "h", " ", "j").Seq

#and also Unicode (enabled by default)

~$ echo -n '3\zKh jÃkΣ' | raku -ne '.comb(/ \\? . /).raku.print'
("3", "\\z", "K", "h", " ", "j", "Ã", "k", "Σ").Seq

펄 참조:
https://perldoc.perl.org
https://www.perl.org

Leku 참고 자료:
https://docs.raku.org
https://raku.org

관련 정보