Estou codificando um script que procura arquivos em um servidor remoto e os transfere de volta para meu computador local. Quero poder fazer uma simulação primeiro, para saber quais arquivos estou trazendo de volta.
Atualmente, estou usando uma combinação de getopts
redirecionamento de saída de algum código que encontrei aqui .
Parece-me, através da minha pesquisa, que é impraticável retornar arrays de funções ZSH ou Bash. Para mim, isso torna difícil entender como eu codificaria esse script sem ter que me repetir muito.
Aqui está o meu script atual:
EDIT: Por favor, perdoe-me misturar alguns bashisms com coisas zsh, comecei a escrever este script usando, #!/bin/bash
mas mudei para zsh.
#!/usr/local/bin/zsh
RED='\033[0;31m'
NC='\033[0m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
dry_run=0
yesterday=1
# Establish -n flag means to do a dry run.
while getopts "ny:" flag; do
case "$flag" in
n) dry_run=1 ;;
y) yesterday=${OPTARG} ;;
*) echo 'error in command line parsing' >&2
exit 1
esac
done
shift $(($OPTIND-1))
# This is the folder I'm interested in getting files from
folder=${1:?"You must define a folder of interest"}
# Check to see if dry-run, if not proceed with copying the files over.
if [ "$dry_run" -eq 1 ]; then
print -Pn "\n%S%11F%{Initiating Dry-Run%}%s%f"
# SSH onto server and find the most recently updated folder.
# Then place that newest folder and folder of interest into the absolute file path.
# Then SSH again and use find with that file-path
# Return array of file paths
# TODO: **THIS IS THE SECTION I NEED TO REFACTOR INTO A FUNCTION**
bison_remote_files=($(
{
{
bison_latest_run=$(ssh -qn falcon1 'find /projects/bison/git/* -mindepth 0 -maxdepth 0 -type d -printf "%T@\t%f\n"' |
sort -t$'\t' -r -nk1,5 |
sed -n "$yesterday"p |
cut -f2-)
bison_remote_path=$(
echo $bison_latest_run |
awk -v folder="$folder" '{print "/projects/bison/git/"$1"/assessment/LWR/validation/"folder}')
ssh -qn falcon1 \
"find $bison_remote_path -type f -name '*_out.csv' -not -path '*/doc/*' 2>/dev/null" >&3 3>&-; echo "$?"
print -Pn "\n\n%U%B%13F%{Fetching data from:%}%u %B%12F%{ /projects/bison/git/${bison_latest_run}%}%b%f\n" >&2
} | {
until read -t1 ret; do
print -Pn "%S%11F%{.%}%s%f" >&2
done
exit "$ret"
}
} 3>&1))
# Maninpulate remote file paths to match the local machine directory
local_file_path=($(for i in "${bison_remote_files[@]}"; do
echo $i |
gsed -E "s|/projects/bison/git/bison_[0-9]{8}|$HOME/Documents/projects/bison|g"
done
))
# Loop through remote and local and show where they will be placed
for ((i=1; i<=${#bison_remote_files[@]}; i++)); do
print -P "\u251C\U2500%B%1F%{Remote File ->%}%b%f ${bison_remote_files[i]}"
print -P "\u251C\U2500%B%10F%{Local File ->%}%b%f ${local_file_path[i]}"
if [[ $i -lt ${#bison_remote_files[@]} ]]; then
print -Pn "\U2502\n"
else
print -Pn "\U2514\U2500\U2504\U27E2\n"
fi
done
# If it's not a dry run, grab all the files using scp
# This is the part I am stuck...
# All my defined variables are un-run in the scope above
# How do I craft a function (or something else) so I don't have to do all the above all over again?
else
printf "${YELLOW}Fetching Data from ${NC}(${GREEN}${bison_latest_run}${NC})${YELLOW}...${NC}\n"
for ((i=0; i<${#NEW_RFILEP[@]}; i++)); do
scp -qp mcdodyla@falcon1:"${NEW_RFILEP[i]}" "${LOCAL_FILEP[i]}"
# Check if scp was successful, if it was show green.
if [ ${PIPESTATUS[0]} -eq 0 ]; then
printf "${GREEN}File Created/Updated at:${NC} ${LOCAL_FILEP[i]}\n"
else
printf "${RED}Error Fetching File:${NC} ${NEW_RFILEP[i]}\n"
fi
done
printf "${YELLOW}Bison Remote Fetch Complete!${NC}\n"
fi
Como você pode ver, todos os meus dados ficam presos no primeiro caso de instrução if e, portanto, se eu não quiser fazer uma simulação, terei que executar todo esse código novamente. Como o bash/zsh realmente não retorna arrays, como refatoro esse código?
EDIT: Aqui está um exemplo de caso de uso:
> bfetch -n "HBEP"
Initiating Dry-Run...
Fetching data from: /projects/bison/git/bison_20190827
├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK370/HBEP_BK370_out.csv
├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK370/HBEP_BK370_out.csv
│
├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK363/HBEP_BK363_out.csv
├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK363/HBEP_BK363_out.csv
│
├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK365/HBEP_BK365_out.csv
├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK365/HBEP_BK365_out.csv
Não sei
zsh
, mas:1) primeiro certifique-se de que todas as suas instruções de impressão "conversacionais" vão para
stderr
, NÃO parastdout
, como:e muitos outros.
2) Em vez de executar suas
scp
instruções, useprintf
-as parastdout
, como:Isso se refere a todas as instruções que modificam o sistema de arquivos, como
cp
,rm
,rsync
,mkdir
,touch
, qualquer que seja. De uma breve inspeção do seu script,scp
foi o único que me chamou a atenção, mas você conhece seu código melhor do que eu.Inspecione seu código novamente e verifique três vezes se todos os comandos de modificação de fs ("irreversíveis") são convertidos em
printf
's. Você não quer perder nenhum.Agora, apenas para testar se você converteu seu script corretamente, execute-o e jogue fora
stderr
:./myscript 2>/dev/null
Isso deve exibir apenas o
stdout
do seu script.Você deve garantir que TODA essa saída seja uma sintaxe de shell válida. Todas as mensagens informativas devem ter ido para
stderr
, e todas as instruções de "ação" devem serprintf
'ed parastdout
. Se você ainda tiver algumas mensagens informativas vazandostdout
, volte e edite seu script novamente e certifique-se de que as instruções de impressão sejam redirecionadas>&2
.Depois de provar definitivamente que as mensagens de informação vão para
stderr
, e o trabalho real vai parastdout
, a sua conversão está concluída.Para executar a seco, basta executar o script:
Para realmente realizar o trabalho, execute o script novamente e canalize
stdout
para um shell:Não sou bom o suficiente com scripts para saber do que diferentes shells são capazes.
No entanto, se o resultado final for que você tenha que repetir todo esse código novamente, você pode construir seu código em um processador de macro como m4 - e usar o processador m4 para expandir a fonte para o script completo.
Por exemplo, se escrever em uma linguagem assembly onde não há arrays, mas tem que percorrer iterativamente os endereços, escreveria a rotina uma vez com algumas variáveis de macro para os endereços fixos, e também no arquivo de macro, defina o ' array' e um loop for - e depois que foi processado por m4, teríamos a fonte repetitiva completa.
Talvez você possa fazer algo assim aqui? ou talvez um pensamento inútil. Apenas uma ideia.