我正在尝试使用 split 创建一个 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
then feed${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
;感谢埃德·莫顿)。
进行试驾:
$ 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//?/(.)}
- 用文字字符串替换每个字符,(.)
从而为每个字符提供一个捕获组(注意:做不是将其用双引号括起来)"${BASH_REMATCH[@]:1}"
- 获取以index == 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 脚本中使用 shell 变量用于替代方案),并且使用 echo 和管道会带来不必要的开销和脆弱性(将根据您使用的字符和 echo 版本而中断)。
charArray
在你的代码中:
charArray=$(echo | awk '...')
是一个标量,而不是一个数组,我认为你的意思是:
charArray=( $(echo | awk '...') )
但是使用命令输出填充数组array=( command )
会将命令输出暴露给 shell 以进行通配和文件名扩展,因此永远不要对任何命令执行此操作,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 的 shell 循环来执行此操作,则为了稳健性和可移植性(假设您使用的是 shell),请执行此操作:
$ 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")
但几乎肯定有一种更好的方法来完成您想做的任何事情,而不是让 shell 循环一次调用 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
Perl 参考资料:
https://perldoc.perl.org
https://www.perl.org