从 bash 中的变量中提取除最后一个字段之外的所有字段
Extract all but last field from a variable in bash
我有一个包含类似以下行的文件:
01/01 THIS IS A DESCRIPTION 123.45
12/23 SHORTER DESC 9.00
11/16 DESC 1,234.00
三个字段:日期、描述、金额。第一个字段将始终跟在 space 之后。最后一个字段将始终以 space 开头。但是中间字段通常会包含 spaces.
我很了解 bash/regex,足以获取第一个和最后一个字段(例如,echo ${LINE##* }
或 cut -f1 -d\
)。但是我如何获得中间字段?基本上所有内容 除了 第一个和最后一个字段。
如果你想删除第一个和最后一个字段,你可以扩展你引用的参数扩展技术:
var=${var#* } var=${var% *}
单个 #
或 %
删除与 glob 匹配的最短子字符串。
您可以尝试以下 awk
:
awk '{="";$NF="";sub(/^[ \t]*/,"")}1' file_name
你可以使用 sed
:
$ sed -E 's/^[^[:space:]]*[[:space:]](.*)[[:space:]][^[:space:]]*$//' file
THIS IS A DESCRIPTION
SHORTER DESC
DESC
或者用 awk:
$ awk '{=$NF=""; sub(/^[ \t]*/,"")}1' file
# same output
您也可以使用cut
和rev
来删除第一个和最后一个字段:
$ cut -d ' ' -f2- file | rev | cut -d ' ' -f2- | rev
# same output
或 GNU grep:
$ grep -oP '^\H+\h\K(.*)(?=\h+\H+$)' file
# same output
或者,使用 Bash 循环和 parameter expansion:
$ while read -r line; do line="${line#* }"; echo "${line% *}"; done <file
# same output
或者,如果您想将字段捕获为 Bash 中的变量:
while IFS= read -r line; do
date="${line%% *}"
amt="${line##* }"
line="${line#* }"
desc="${line% *}"
printf "%5s %10s \"%s\"\n" "$date" "$amt" "$desc"
done <file
打印:
01/01 123.45 "THIS IS A DESCRIPTION"
12/23 9.00 "SHORTER DESC"
11/16 1,234.00 "DESC"
bash:将行读入一个单词数组,从数组中挑选出想要的元素
while read -ra words; do
date=${words[0]}
amount=${words[-1]}
description=${words[*]:1:${#words[@]}-2}
printf "%s=%s\n" date "$date" desc "$description" amt "$amount"
done < file
产出
date=01/01
desc=THIS IS A DESCRIPTION
amt=123.45
date=12/23
desc=SHORTER DESC
amt=9.00
date=11/16
desc=DESC
amt=1,234.00
这是有趣的一点:${words[*]:1:${#words[@]}-2}
- 取单词数组的 slice,从索引 1(第二个元素)开始 length of "number of elements minus 2"
- 这些词将通过 space 分隔符连接成一个字符串。
查看 Shell Parameter Expansion 并向下滚动一点以查看 ${parameter:offset:length}
讨论。
如果你想在 bash 中使用正则表达式,那么你可以使用捕获括号和 BASH_REMATCH 数组
while IFS= read -r line; do
if [[ $line =~ ([^[:blank:]]+)" "(.+)" "([^[:blank:]]+) ]]; then
echo "date=${BASH_REMATCH[1]}"
echo "desc=${BASH_REMATCH[2]}"
echo "amt=${BASH_REMATCH[3]}"
fi
done < file
与上面相同的输出。
注意模式中 space 需要被引用(或反斜杠转义)
我有一个包含类似以下行的文件:
01/01 THIS IS A DESCRIPTION 123.45
12/23 SHORTER DESC 9.00
11/16 DESC 1,234.00
三个字段:日期、描述、金额。第一个字段将始终跟在 space 之后。最后一个字段将始终以 space 开头。但是中间字段通常会包含 spaces.
我很了解 bash/regex,足以获取第一个和最后一个字段(例如,echo ${LINE##* }
或 cut -f1 -d\
)。但是我如何获得中间字段?基本上所有内容 除了 第一个和最后一个字段。
如果你想删除第一个和最后一个字段,你可以扩展你引用的参数扩展技术:
var=${var#* } var=${var% *}
单个 #
或 %
删除与 glob 匹配的最短子字符串。
您可以尝试以下 awk
:
awk '{="";$NF="";sub(/^[ \t]*/,"")}1' file_name
你可以使用 sed
:
$ sed -E 's/^[^[:space:]]*[[:space:]](.*)[[:space:]][^[:space:]]*$//' file
THIS IS A DESCRIPTION
SHORTER DESC
DESC
或者用 awk:
$ awk '{=$NF=""; sub(/^[ \t]*/,"")}1' file
# same output
您也可以使用cut
和rev
来删除第一个和最后一个字段:
$ cut -d ' ' -f2- file | rev | cut -d ' ' -f2- | rev
# same output
或 GNU grep:
$ grep -oP '^\H+\h\K(.*)(?=\h+\H+$)' file
# same output
或者,使用 Bash 循环和 parameter expansion:
$ while read -r line; do line="${line#* }"; echo "${line% *}"; done <file
# same output
或者,如果您想将字段捕获为 Bash 中的变量:
while IFS= read -r line; do
date="${line%% *}"
amt="${line##* }"
line="${line#* }"
desc="${line% *}"
printf "%5s %10s \"%s\"\n" "$date" "$amt" "$desc"
done <file
打印:
01/01 123.45 "THIS IS A DESCRIPTION"
12/23 9.00 "SHORTER DESC"
11/16 1,234.00 "DESC"
bash:将行读入一个单词数组,从数组中挑选出想要的元素
while read -ra words; do
date=${words[0]}
amount=${words[-1]}
description=${words[*]:1:${#words[@]}-2}
printf "%s=%s\n" date "$date" desc "$description" amt "$amount"
done < file
产出
date=01/01
desc=THIS IS A DESCRIPTION
amt=123.45
date=12/23
desc=SHORTER DESC
amt=9.00
date=11/16
desc=DESC
amt=1,234.00
这是有趣的一点:${words[*]:1:${#words[@]}-2}
- 取单词数组的 slice,从索引 1(第二个元素)开始 length of "number of elements minus 2"
- 这些词将通过 space 分隔符连接成一个字符串。
查看 Shell Parameter Expansion 并向下滚动一点以查看 ${parameter:offset:length}
讨论。
如果你想在 bash 中使用正则表达式,那么你可以使用捕获括号和 BASH_REMATCH 数组
while IFS= read -r line; do
if [[ $line =~ ([^[:blank:]]+)" "(.+)" "([^[:blank:]]+) ]]; then
echo "date=${BASH_REMATCH[1]}"
echo "desc=${BASH_REMATCH[2]}"
echo "amt=${BASH_REMATCH[3]}"
fi
done < file
与上面相同的输出。
注意模式中 space 需要被引用(或反斜杠转义)