给输入的单行带有'n'参数,它们之间用空格分隔。输入参数本身是可变的。输入是通过外部文件给出的。
我想根据正则表达式将特定元素移至变量。因此,我正在考虑先声明一个指针变量,以跟踪我所在的行。另外,变量的分配与数字顺序无关,并且取决于输入,某些变量可能会被完全跳过。
我目前的方法是使用
awk '{print $1}' file.txt
但是,并非所有元素都是固定的,因此我需要考虑可能不存在或可能具有多个条目的元素。
更新:我找到了另一种方法。
file=$(cat /file.txt)
for i in ${file[@]}; do
echo $i >> split.txt;
done
通过这种方式,我们得到了带有单个参数的多行,而不是带有多个参数的单行。这样,我们现在可以使用var#=(grep --regexp="[pattern]" split.txt
。现在,我只需要弄清楚如何最好地使用正则表达式来过滤这种混乱情况。
让我举个例子。
我的输入字符串是:
RON KKND 1534Z AUTO 253985G 034SRT 134OVC 04/32
RON KKND 5256Z 143623G72K 034OVC 074OVC 134SRT 145PRT 13/00
RON KKND 2234Z CON 342523G CLS 01/M12 RMK
因此,上述每个变量的赋值为:
var1=RON var2=KKND var3=1534Z var4=TRUE var5=FALSE var6=253985G varC=2 varC1=034SRT varC2=134OVC var7=04/32
var1=RON var2=KKND var3=5256Z var4=FALSE var5=FALSE var6=143623G72K varC=4 varC1=034OVC varC2=074OVC varC3=134SRT varC4=145PRT var7=13/00
var1=RON var2=KKND var3=2234Z var4=FALSE var5=TRUE var6=342523G varC=0 var7=01/M12
因此,第四个参数可能是var4,var5或var6。
第五个参数可以是var5,var6或与其他条件匹配。
第六个参数可以是var6,也可以不是。可以通过将每个参数与*/*
将其归结为更多,var1,var2和var3的输入位置是固定的,但此后我需要比较,排序和分配。此外,参数本身的字符长度可以不同。要分割的每个部分的相对位置相对于其相邻部分是固定的。例如,在输入中var7绝不会在var6之前,如果var4和var5为true,则第4个和第5个自变量始终为'AUTO CON'。某些段始终为一个自变量,而其他段则始终为一个以上。每个的相对位置是已知的。对于每种模式,某些模式在特定位置具有特定字符,而其他模式除了在序列中的位置之外,可能没有任何标志。
所以我需要awk来识别一个指针变量,因为需要检查每个参数,直到找到特定的匹配项为止
#Check to see if var4 or var5 exists. if so, flag and increment pointer
pointer=4
if (awk '{print $$pointer}' file.txt) == "AUTO" ; then
var4="TRUE"
pointer=$pointer+1
else
var4="FALSE"
fi
if (awk '{print $$pointer}' file.txt) == "CON" ; then
var5="TRUE"
pointer=$pointer+1
else
var5="FALSE"
fi
#position of var6 is fixed once var4 and var5 are determined
var6=$(awk '{print $$pointer}' file.txt)
pointer=$pointer+1
#Count the arguments between var6 and var7 (there may be up to ten)
#and separate each to decode later. varC[0-9] is always three upcase
# letters followed by three numbers. Use this counter later when decoding.
varC=0
until (awk '{print $$pointer}' file.txt) == "*/*" ; do
varC($varC+1)=(awk '{print $$pointer}' file.txt)
varC=$varC+1
pointer=$pointer+1
done
#position of var7 is fixed after all arguments of varC are handled
var7=$(awk '{print $$pointer}' file.txt)
pointer=$pointer+1
我知道以上语法不正确。问题是我该如何解决。
var7并不总是在输入行的末尾。但是,无需处理var7之后的参数。
实际上是解释我还没有想到的模式。我打算使用用case语句比较变量和正则表达式来进行处理。我不想使用awk直接解释模式,因为那样会很混乱。我已经考虑过使用for n in $string
,但是这样做意味着直接将每个参数与每个可能的组合进行比较(并且有多个段,每个段都有多个模式),这是不切实际的。我正在尝试将其分为两个步骤。
答案 0 :(得分:0)
已更新:此代码显示了如何多次基于模式匹配确定变量值。
一个代码块以纯bash格式,另一个以gawk方式
bash代码块需要关联数组支持,在早期版本中不可用
还需要grep
进行模式匹配
经过GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
和grep (GNU grep) 2.20
测试
并在我学会了why-is-printf-better-than-echo后坚持学习printf
,而不是echo
在使用bash时,我认为最好的做法是提高防御力
#!/bin/bash
declare -ga outVars
declare -ga lineBuf
declare -g NF
#force valid index starts from 1
#consistent with var* name pattern
outVars=(unused var1 var2 var3 var4 var5 var6 varC var7)
((numVars=${#outVars[@]} - 1))
declare -gr numVars
declare -r outVars
function e_unused {
return
}
function e_var1 {
printf "%s" "${lineBuf[1]}"
}
function e_var2 {
printf "%s" "${lineBuf[2]}"
}
function e_var3 {
printf "%s" "${lineBuf[3]}"
}
function e_var4 {
if [ "${lineBuf[4]}" == "AUTO" ] ;
then
printf "TRUE"
else
printf "FALSE"
fi
}
function e_var5 {
if [ "${lineBuf[4]}" == "CON" ] ;
then
printf "TRUE"
else
printf "FALSE"
fi
}
function e_varC {
local var6_idx=4
if [ "${lineBuf[4]}" == "AUTO" -o "${lineBuf[4]}" == "CON" ] ;
then
var6_idx=5
fi
local var7_idx=$NF
local i
local count=0
for ((i=NF;i>=1;i--));
do
if [ $(grep -cE '^.*/.*$' <<<${lineBuf[$i]}) -eq 1 ];
then
var7_idx=$i
break
fi
done
((varC = var7_idx - var6_idx - 1))
if [ $varC -eq 0 ];
then
printf 0
return;
fi
local cFamily=""
local append
for ((i=var6_idx;i<=var7_idx;i++));
do
if [ $(grep -cE '^[0-9]{3}[A-Z]{3}$' <<<${lineBuf[$i]}) -eq 1 ];
then
((count++))
cFamily="$cFamily varC$count=${lineBuf[$i]}"
fi
done
printf "%s %s" $count "$cFamily"
}
function e_var6 {
if [ "${lineBuf[4]}" == "AUTO" -o "${lineBuf[4]}" == "CON" ] ;
then
printf "%s" "${lineBuf[5]}"
else
printf "%s" "${lineBuf[4]}"
fi
}
function e_var7 {
local i
for ((i=NF;i>=1;i--));
do
if [ $(grep -cE '^.*/.*$' <<<${lineBuf[$i]}) -eq 1 ];
then
printf "%s" "${lineBuf[$i]}"
return
fi
done
}
while read -a lineBuf ;
do
NF=${#lineBuf[@]}
lineBuf=(unused ${lineBuf[@]})
for ((i=1; i<=numVars; i++));
do
printf "%s=" "${outVars[$i]}"
(e_${outVars[$i]})
printf " "
done
printf "\n"
done <file.txt
下面的awk代码中使用了gawk特定的扩展名Indirect Function Call
该代码为每个所需的输出变量分配一个函数名称。
可以在其特定功能中应用不同的模式或其他转换
这样做是为了避免大量if-else-if-else
并且更易于阅读和扩展。
对于特殊的varC系列,函数pick_varC
发挥了作用
确定varC之后,其值包含多个输出字段。
如果varC=2
,则varC的值返回为2 varC1=034SRT varC2=134OVC
这是varC附加所有跟随成员的实际值。
gawk '
BEGIN {
keys["var1"] = "pick_var1";
keys["var2"] = "pick_var2";
keys["var3"] = "pick_var3";
keys["var4"] = "pick_var4";
keys["var5"] = "pick_var5";
keys["var6"] = "pick_var6";
keys["varC"] = "pick_varC";
keys["var7"] = "pick_var7";
}
function pick_var1 () {
return $1;
}
function pick_var2 () {
return $2;
}
function pick_var3 () {
return $3;
}
function pick_var4 () {
for (i=1;i<=NF;i++) {
if ($i == "AUTO") {
return "TRUE";
}
}
return "FALSE";
}
function pick_var5 () {
for (i=1;i<=NF;i++) {
if ($i == "CON") {
return "TRUE";
}
}
return "FALSE";
}
function pick_varC () {
for (i=1;i<=NF;i++) {
if (($i=="AUTO" || $i=="CON")) {
break;
}
}
var6_idx = 5;
if ( i!=4 ) {
var6_idx = 4;
}
var7_idx = NF;
for (i=1;i<=NF;i++) {
if ($i~/.*\/.*/) {
var7_idx = i;
}
}
varC = var7_idx - var6_idx - 1;
if ( varC == 0) {
return varC;
}
count = 0;
cFamily = "";
for (i = 1; i<=varC;i++) {
if ($(var6_idx+i)~/[0-9]{3}[A-Z]{3}/) {
cFamily = sprintf("%s varC%d=%s",cFamily,i,$(var6_idx+i));
count++;
}
}
varC = sprintf("%d %s",count,cFamily);
return varC;
}
function pick_var6 () {
for (i=1;i<=NF;i++) {
if (($i=="AUTO" || $i=="CON")) {
break;
}
}
if ( i!=4 ) {
return $4;
} else {
return $5
}
}
function pick_var7 () {
for (i=1;i<=NF;i++) {
if ($i~/.*\/.*/) {
return $i;
}
}
}
{
for (k in keys) {
pickFunc = keys[k];
printf("%s=%s ",k,@pickFunc());
}
printf("\n");
}
' file.txt
测试输入
RON KKND 1534Z AUTO 253985G 034SRT 134OVC 04/32
RON KKND 5256Z 143623G72K 034OVC 074OVC 134SRT 145PRT 13/00
RON KKND 2234Z CON 342523G CLS 01/M12 RMK
脚本输出
var1=RON var2=KKND var3=1534Z var4=TRUE var5=FALSE varC=2 varC1=034SRT varC2=134OVC var6=253985G var7=04/32
var1=RON var2=KKND var3=5256Z var4=FALSE var5=FALSE varC=4 varC1=034OVC varC2=074OVC varC3=134SRT varC4=145PRT var6=143623G72K var7=13/00
var1=RON var2=KKND var3=2234Z var4=FALSE var5=TRUE varC=0 var6=342523G var7=01/M12
答案 1 :(得分:0)
请尝试以下操作:
#!/bin/bash
# template for variable names
declare -a namelist1=( "var1" "var2" "var3" "var4" "var5" "var6" "varC" )
declare -a ary
# read each line and assign ary to the elements
while read -r -a ary; do
if [[ ${ary[3]} = AUTO ]]; then
ary=( "${ary[@]:0:3}" "TRUE" "FALSE" "${ary[4]}" "" "${ary[@]:5:3}" )
elif [[ ${ary[3]} = CON ]]; then
ary=( "${ary[@]:0:3}" "FALSE" "TRUE" "${ary[4]}" "" "${ary[@]:5:3}" )
else
ary=( "${ary[@]:0:3}" "FALSE" "FALSE" "${ary[3]}" "" "${ary[@]:4:5}" )
fi
# initial character of the 7th element
ary[6]=${ary[7]:0:1}
# locate the index of */* entry in the ary and adjust the variable names
for (( i=0; i<${#ary[@]}; i++ )); do
if [[ ${ary[$i]} == */* ]]; then
declare -a namelist=( "${namelist1[@]}" )
for (( j=1; j<=i-7; j++ )); do
namelist+=( "$(printf "varC%d" "$j")" )
done
namelist+=( "var7" )
fi
done
# assign variables to array elements
for (( i=0; i<${#ary[@]}; i++ )); do
# echo -n "${namelist[$i]}=${ary[$i]} " # for debugging
declare -n p="${namelist[$i]}"
p="${ary[$i]}"
done
# echo "var1=$var1 var2=$var2 var3=$var3 ..." # for debugging
done < file.txt
请注意,上面的脚本仅分配bash变量,不打印任何内容
除非您明确地echo
或printf
变量。