csv Bash for循环一次处理所有文件,而不是一个接一个地处理

vof42yt1  于 2023-03-10  发布在  其他
关注(0)|答案(2)|浏览(154)

我遇到了一个奇怪的问题,不知道它来自哪里
我有2个类似的功能:

TMP_DIR="/tmp/folder"
OUTPUT_DIR="output"

func1()
{
   for file in `ls $TMP_DIR/*_INPUT_VLAN.csv`
   do
     local hostname=$(echo $file | awk -F '_INPUT' '{print $1}' | cut -d "/" -f 4)
     OUTPUT=$(echo $hostname"_net_conf.txt")
     IFS=";"
     cat $file | sed '1d' | while read f1 f2 f3 f4 f5 f6 f7 f8
     do
       echo "some things with $f1 $f2 .." >> $OUTPUT_DIR/$OUTPUT
     done
   done
}

func2()
{
   for file in `ls $TMP_DIR/*_INPUT_ROUTES.csv`
   do
     local hostname=$(echo $file | awk -F '_INPUT' '{print $1}' | cut -d "/" -f 4)
     OUTPUT=$(echo $hostname"_net_conf.txt")
     IFS=";"
     cat $file | sed '1d' | while read f1 f2 f3 f4
     do
       echo "some things with $f1 $f2 .." >> $OUTPUT_DIR/$OUTPUT
     done
   done
}

输入文件如下所示:
文件1:主机名1输入虚拟局域网. csv

int;type;vxid;name;id;ip;mask;comment
int1;int;0;vlan1;1;192.168.1.1;255.255.255.0;VLAN Network 1
int2;ext;1;vlan2;1;192.168.2.1;255.255.255.0;VLAN Network 2
int3;int;2;vlan3;1;192.168.3.1;255.255.255.0;VLAN Network 2

文件2:主机名2输入虚拟局域网. csv

int;type;vxid;name;id;ip;mask;comment
int1;int;0;vlan1;1;192.168.1.1;255.255.255.0;VLAN Network 1
int2;ext;1;vlan2;1;192.168.2.1;255.255.255.0;VLAN Network 2
int3;int;2;vlan3;1;192.168.3.1;255.255.255.0;VLAN Network 2

文件1:主机名1_INPUT_ROUTES.csv

Network;Interface;Gateway;Comment
0.0.0.0/0;int1;gateway1;Comment 1
net_1;int2;gateway2;Comment2
net_2;int2;gateway2;Comment2,1
net_3;int3;gateway3;Comment3

文件2:主机名2_INPUT_ROUTES.csv

Network;Interface;Gateway;Comment
0.0.0.0/0;int1;gateway1;Comment 1
net_1;int2;gateway2;Comment2
net_2;int2;gateway2;Comment2,1
net_3;int3;gateway3;Comment3

当我运行脚本时,第一个函数成功了,但第二个函数似乎没有执行for循环,而是尝试同时对两个文件执行cat操作:

cat: '/tmp/folder/HOSTNAME1_INPUT_ROUTES.csv'$'\n''/tmp/folder/HOSTNAME2_INPUT_ROUTES.csv': No such file or directory

如果我在函数中有一些回声来查看发生了什么,我会看到两个文件同时进行

func1()
{
  for file in `ls $TMP_DIR/*_INPUT_VLAN.csv`
  do
    local hostname=$(echo $file | awk -F '_INPUT' '{print $1}' | cut -d "/" -f 4)
    OUTPUT=$(echo $hostname"_net_conf.txt")
    echo "Hostname for $file will be : $hostname"
    echo "Ouput for $file will be located in : $OUTPUT_DIR/$OUTPUT"
  done
}

function2()
{
  for file in `ls $TMP_DIR/*_INPUT_ROUTES.csv`
  do
    local hostname=$(echo $file | awk -F '_INPUT' '{print $1}' | cut -d "/" -f 4)
    OUTPUT=$(echo $hostname"_net_conf.txt")
    echo "Hostname for $file will be : $hostname"
    echo "Ouput for $file will be located in : $OUTPUT_DIR/$OUTPUT"
  done
}

输出:

Hostname for /tmp/folder/HOSTNAME1_INPUT_VLAN.csv will be : HOSTNAME1
Ouput for /tmp/folder/HOSTNAME1_INPUT_VLAN.csv will be located in : output/HOSTNAME1_net_conf.txt

Hostname for /tmp/folder/HOSTNAME1_INPUT_ROUTES.csv
/tmp/folder/HOSTNAME2_INPUT_ROUTES.csv will be : HOSTNAME1
HOSTNAME2
Ouput for /tmp/folder/HOSTNAME1_INPUT_ROUTES.csv
/tmp/folder/HOSTNAME2_INPUT_ROUTES.csv will be located in : output/HOSTNAME1
HOSTNAME2_net_conf.txt

cat: '/tmp/folder/HOSTNAME1_INPUT_ROUTES.csv'$'\n''/tmp/folder/HOSTNAME2_INPUT_ROUTES.csv': No such file or directory

两个函数都用相同的why语句执行for循环,我不明白为什么func2不像excepted那样继续。
我找到了一个变通方案,方法是列出目录中的文件,存储在一个tmp文件中,然后使用while循环阅读该tmp文件,但我想知道为什么原始的for循环不起作用
预先感谢你的帮助

xn1cxnb4

xn1cxnb41#

func1将IFS设置为;,这样func2ls $TMP_DIR/*_INPUT_ROUTES.csv的输出就不再在换行符上拆分。IFS控制如何执行单词拆分,所以在修改它时需要非常小心。
一些一般性建议:

for file in "$TMP_DIR"/*_INPUT_VLAN.csv ; do
    ...
  done
  • 尽可能使用限定作用域的环境变量或子shell来本地化IFS更改。例如,如果您只需要为while-loop更改IFS,则使用此方法。通过这种方式,对IFS的更改将仅限于您的read命令。
cat $file | sed '1d' | while IFS=';' read f1 f2 f3 f4 f5 f6 f7 f8
     do
       echo "some things with $f1 $f2 .." >> $OUTPUT_DIR/$OUTPUT
     done
  • shellcheck中运行你的脚本来寻找一些常见的问题,它会突出一些常见的问题,比如ls解析问题。
d7v8vwbk

d7v8vwbk2#

此问题似乎与在第二个函数func2()while循环内的cat命令中使用管道|有关。
管道创建了一个独立于主shell运行的子shell,在子shell中声明的任何变量在外部都不可用。
因此,$OUTPUT变量在 shell 程序中没有正确设置,并且不是重定向到具有预期名称的文件,而是重定向到具有两个$hostname变量的名称并置的文件。
要解决此问题,可以尝试在cat命令中将管道|替换为<,如下所示:

while IFS=";" read f1 f2 f3 f4; do
  echo "some things with $f1 $f2 .." >> "$OUTPUT_DIR/$OUTPUT"
done < "$file"

这将重定向while循环的输入,使其来自$file指定的文件,而不是管道创建的子shell。
此外,通常建议使用$(command)而不是反勾号进行命令替换,因此可以考虑将ls $TMP_DIR/*_INPUT_VLAN.csv替换为$(ls $TMP_DIR/*_INPUT_VLAN.csv),对于其他使用反勾号的命令也是如此。
希望有帮助

相关问题