Остановка цикла Bash после первой итерации

#bash #while-loop #iteration

Вопрос:

Мне нужно написать сценарий для копирования содержимого с удаленного сервера на локальную машину. Хотя есть много способов сделать это, поскольку серверы принадлежат к разным кластерам (один не поддерживает другой), мои коллеги посоветовали использовать простой скрипт 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

 

Комментарии:

1. см. mywiki. wooledge.org/BashFAQ/089

Ответ №1:

@pynexj указал на ссылку с <a rel=»noreferrer noopener nofollow» href=»https://mосновной причиной проблемы.

При построчном чтении файла, если команда внутри цикла также считывает stdin, она может исчерпать входной файл. В этом примере строка:

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

считывал stdin в цикле, считывая из stdin, эффективно исчерпывая ввод.

Похоже, что решение этой проблемы заключается в явном перенаправлении stdin, а в случае ssh это можно сделать с помощью переключателя-n.

  -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 amp; 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.)
 

Сценарий работает с флагом-n, добавленным во внутреннюю команду ssh, оставляя окончательный сценарий в виде:

 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