【问题标题】:Bash loop stopping after first iteration第一次迭代后 Bash 循环停止
【发布时间】:2023-10-09 00:50:01
【问题描述】:

我需要编写一个脚本来将内容从远程服务器复制到本地计算机。尽管有很多方法可以做到这一点,因为服务器属于不同的集群(一个是 kerborized 另一个不是),我的同事建议使用简单的 bash 脚本将数据从远程位置流式传输到本地计算机(它所在的位置)流式传输到 hdfs put 命令以将其存储在本地集群上)。

以下代码适用于第一次迭代,创建一个目录、一个正确命名的文件并向其中添加正确的内容。但是它在第一个文件之后停止(在这个测试场景中,有三个文件名为 a、b 和 c,并且文件 a 在目标服务器中成功创建,内容正确,但文件 b 和 c 不是。

谁能帮助理解这个脚本出了什么问题?我认为这个问题与我的 bash 语法有关,而不是它背后更复杂的 hdfs 的问题。

这是脚本:

lastIterationDir=""

ssh -i <private_key> <user>@<source_ip> "hdfs dfs -ls -R <sourceDr>" | 
  awk ' $0 !~ /^d/ {print $NF}'  |  
  while read fileToTransfer
do
    thisIterationDir=`dirname $fileToTransfer}`
    
    if [ "$thisIterationDir" != "$lastIterationDir" -a "$thisIterationDir" != "/" ]; then
        hdfs dfs -mkdir -p "/path/to/save/transfered/files${thisIterationDir}"
    fi
    
    ssh -i <private_key> <user>@<source_ip> "hdfs dfs -cat ${fileToTransfer}" | 
      hdfs dfs -put - "${destinationFolder}${fileToTransfer}"
    lastIterationDir=$thisIterationDir
done

【问题讨论】:

标签: bash while-loop iteration


【解决方案1】:

@pynexj 指向带有root cause of the problem 的链接。

当逐行读取文件时,如果循环内的命令也读取标准输入,它可能会耗尽输入文件。在本例中,行:

ssh -i <private_key> <user>@<source_ip> "hdfs dfs -cat ${fileToTransfer}" | 
  hdfs dfs -put - "${destinationFolder}${fileToTransfer}"

在从标准输入读取的循环中读取标准输入,有效地耗尽了输入。

似乎解决这个问题的方法在于明确重定向标准输入和the case of ssh, this can be done by using the -n switch

 -n      Redirects stdin from /dev/null (actually, prevents reading
         from stdin).  This must be used when ssh is run in the back‐
         ground.  A common trick is to use this to run X11 programs
         on a remote machine.  For example, ssh -n shadows.cs.hut.fi
         emacs & will start an emacs on shadows.cs.hut.fi, and the
         X11 connection will be automatically forwarded over an
         encrypted channel.  The ssh program will be put in the back‐
         ground.  (This does not work if ssh needs to ask for a pass‐
         word or passphrase; see also the -f option.)

该脚本与添加到内部 ssh 命令的 -n 标志一起使用,最终脚本为:

lastIterationDir=""

ssh -i <private_key> <user>@<source_ip> "hdfs dfs -ls -R <sourceDr>" |
  awk ' $0 !~ /^d/ {print $NF}' |
  while read fileToTransfer
do
    thisIterationDir=`dirname $fileToTransfer}`

    if [ "$thisIterationDir" != "$lastIterationDir" -a "$thisIterationDir" != "/" ]; then
        hdfs dfs -mkdir -p "/path/to/save/transfered/files${thisIterationDir}"
    fi

    ssh -i <private_key> <user>@<source_ip> -n "hdfs dfs -cat ${fileToTransfer}" | 
      hdfs dfs -put - "${destinationFolder}${fileToTransfer}"

    lastIterationDir=$thisIterationDir
done

【讨论】: