분할을 사용하여 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