【问题标题】:While loop fail to read all line from file虽然循环无法从文件中读取所有行
【发布时间】:2021-12-13 13:37:38
【问题描述】:

所以我得到了这个从远程服务器执行备份命令的 bash 脚本,每个服务器主机名都在名为 /tmp/quarantine.list 的文件中定义。内容是这样的:

server1
server2

server1server2 是我的远程服务器的 ssh 别名,所以如果我需要访问其中任何一个,我只需运行例如ssh server1.

我需要备份/tmp/quarantine.list 中的每个服务器,所以我使用while 循环循环每一行。这是我的代码。

    # Reading from /tmp/quarantine.list
while IFS= read -r list; do
  if [ "$list" == "server4" ]; then
    users="adminjoe"
  fi
  tempfile="${logdir}/rsync_$list.log"
  SECONDS=0
  set +x
  if rsync -ca --stats --info=progress2 --remove-source-files $list:/home/${users}/backup/20* $backup_location/$list >> $tempfile 2>&1; then
    ssh $list "find /home/${users}/backup/20* -type d -empty -delete"
  else
    echo "Error while running rsync"
    exit 1
  fi
  set -x
  elapsed_time=$SECONDS
  transferred_size=$(gawk 'BEGIN { FS=":" } {gsub(/^[ \t]+|[ \t]+$/, "", $2)} /Total transferred file size/ { print $2 }' $tempfile | tr -cd "[:digit:]")
  echo "Backup of $list done at $(/bin/date +'%Y-%m-%d_%H:%M:%S') --> $(bytesToHumanReadable $transferred_size)" | tee -a "${HOME}/backup.log"
  echo "<b>$list --> $(bytesToHumanReadable $transferred_size)</b>" >> $konten
  echo "$transferred_size $list" >> $summaryfile_size
  echo "$elapsed_time $list" >> $summaryfile_time
done < "/tmp/quarantine.list"
rm "/tmp/quarantine.list"

问题是我的脚本只选择/tmp/quarantine.list 的第一行,例如如果有两个远程隔离区。

server2
server6

脚本只处理server2,而server6 保持不变。奇怪的是,当我将--dry-run 选项设置为rsync 时,它会毫无问题地处理/tmp/quarantine.list 中的所有行!

没有--dry-run

+ echo 'Backup of server2 done at 2021-10-28_16:55:26 --> 3.28 GB'
Backup of server2 done at 2021-10-28_16:55:26 --> 3.28 GB
++ bytesToHumanReadable 3280769140
++ S=("bytes" "kB" "MB" "GB" "TB" "PB" "EB" "YB" "ZB")
++ local i=3280769140 d= s=0 S
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 14
++ i=3280769
++ s=1
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 76
++ i=3280
++ s=2
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 28
++ i=3
++ s=3
++ (( i > 1000 && s < 9-1 ))
++ echo '3.28 GB'
+ echo '<b>server2 --> 3.28 GB</b>'
+ echo '3280769140 server2'
+ echo '1883 server2'
+ IFS=
+ read -r list
+ rm /tmp/quarantine.list

--dry-run

+ echo 'Backup of server2 done at 2021-10-28_16:07:14 --> 3.28 GB'
Backup of server2 done at 2021-10-28_16:07:14 --> 3.28 GB
++ bytesToHumanReadable 3280769140
++ S=("bytes" "kB" "MB" "GB" "TB" "PB" "EB" "YB" "ZB")
++ local i=3280769140 d= s=0 S
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 14
++ i=3280769
++ s=1
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 76
++ i=3280
++ s=2
++ (( i > 1000 && s < 9-1 ))
++ printf -v d .%02d 28
++ i=3
++ s=3
++ (( i > 1000 && s < 9-1 ))
++ echo '3.28 GB'
+ echo '<b>server2 --> 3.28 GB</b>'
+ echo '3280769140 server2'
+ echo '32 server2'
+ IFS=
+ read -r list
+ '[' server6 == server4 ']'

如您所见,后者继续使用server6,而不是前者。我只是想知道我在这里做错了什么?

【问题讨论】:

  • 会不会是您在最后一行的末尾缺少换行符?
  • @user1934428 不,我已经检查了文件内容。我可以使用while 手动循环它们。
  • 在您的情况下,IFS= 有什么意义?我认为这会导致整个文件已经在第一次迭代中被读取。见[这里]/bash.cyberciti.biz/guide/$IFS)
  • -n 添加到您的ssh 命令中?
  • @Cyrus 添加-n 选项可解决问题!

标签: bash loops


【解决方案1】:

如果文件的最后一行没有以换行符结尾,就会发生这种情况。然后read 将实际读取该行,但它会返回一个非零状态(参考:https://www.gnu.org/software/bash/manual/bash.html#index-read)。这种非零状态导致while 循环结束。

处理这个问题的惯用方法是:

while IFS= read -r list || [[ -n $list ]]; do

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 2011-12-03
    • 2019-03-19
    相关资源
    最近更新 更多