Posted on: August 13, 2021 11:00 PM
Posted by: Renato
Views: 1330
Como escrever um loop Bash Shell sobre um conjunto de arquivos
Como executo o loop de shell sobre um conjunto de arquivos armazenados em um diretório atual ou diretório especificado?
Você pode usar o loop for facilmente sobre um conjunto de arquivos de shell no bash ou qualquer outro shell do UNIX usando o caractere curinga.
Tutorial details | |
---|---|
Difficulty level | Easy |
Root privileges | No |
Requirements | Bash under Linux, macOS or Unix |
Est. reading time | 3 minutes |
Syntax
A sintaxe geral é a seguinte:
for f in file1 file2 file3 file5 do echo "Processing $f" # do something on $f done
Você também pode usar variáveis de shell:
FILES="file1 /path/to/file2 /etc/resolv.conf" for f in $FILES do echo "Processing $f" done
Você pode percorrer todos os arquivos, como * .c, digite:
$ for f in *.c; do echo "Processing $f file.."; done
Amostra de script de shell para percorrer todos os arquivos
#!/bin/bash # NOTE : Quote it else use array to avoid problems # FILES="/path/to/*" for f in $FILES do echo "Processing $f file..." # take action on each file. $f store current file name cat "$f" done
Como verificar se o arquivo não existe no Bash
- https://www.cyberciti.biz/faq/bash-check-if-file-does-not-exist-linux-unix/
Podemos descobrir se existe um arquivo com expressões condicionais em um shell Bash. Em outras palavras, bash for loop não funcionará cegamente em arquivos. Pense nisso como à prova de falhas:
#!/bin/bash FILES="/etc/sys*.conf" for f in $FILES do # FAILSAFE # # Check if "$f" FILE exists and is a regular file and then only copy it # if [ -f "$f" ] then echo "Processing $f file..." cp -f "$f" /dest/dir else echo "Warning: Some problem with \"$f\"" fi done
Expansão de nome de arquivo
Você pode fazer a expansão do nome do arquivo em loop, como trabalhar em todos os arquivos PDF no diretório atual:
for f in *.pdf do echo "Removing password for pdf file - $f" # always "double quote" $f to avoid problems /path/to/command --option "$f" done
No entanto, há um problema com a sintaxe acima. Se não houver arquivos PDF no diretório atual, ele se expandirá para * .pdf (ou seja, f será definido como * .pdf ”). Para evitar esse problema, adicione a seguinte instrução antes do loop for:
#!/bin/bash # Usage: remove all utility bills pdf file password shopt -s nullglob for f in *.pdf do echo "Removing password for pdf file - $f" pdftk "$f" output "output.$f" user_pw "YOURPASSWORD-HERE" done # unset it now shopt -u nullglob # rest of the script below
Usando uma variável Shell e loop while
Você pode ler a lista de arquivos de um arquivo de texto. Por exemplo, crie um arquivo de texto chamado /tmp/data.txt da seguinte maneira:
file1 file2 file3
Agora você pode usar o loop while da seguinte maneira para ler e processar um por um:
#!/bin/bash while IFS= read -r file do [ -f "$file" ] && rm -f "$file" done < "/tmp/data.txt"
Aqui está outro exemplo que remove todos os arquivos indesejados de lighttpd / nginx chrootado ou servidor da web Apache:
#!/bin/bash _LIGHTTPD_ETC_DEL_CHROOT_FILES="/usr/local/nixcraft/conf/apache/secure/db/dir.etc.list" secureEtcDir(){ local d="$1" local _d="/jails/apache/$d/etc" local __d="" [ -f "$_LIGHTTPD_ETC_DEL_CHROOT_FILES" ] || { echo "Warning: $_LIGHTTPD_ETC_DEL_CHROOT_FILES file not found. Cannot secure files in jail etc directory."; return; } echo "* Cleaning etc FILES at: \"$_d\" ..." while IFS= read -r file do __d="$_d/$file" [ -f "$__d" ] && rm -f "$__d" done < "$_LIGHTTPD_ETC_DEL_CHROOT_FILES" } secureEtcDir "nixcraft.net.in"
Processando argumentos de linha de comando
Aqui está outro exemplo simples:
#!/bin/bash # make sure you always put $f in double quotes to avoid any nasty surprises i.e. "$f" # we use "$@" (with double quotes) instead of $* or $@ because # we want to prevent whitespace problems with filenames. for f in "$@" do echo "Processing $f file..." # rm "$f" done
O seguinte não funcionará, pois citamos duas vezes "$ *". Em outras palavras, não haverá divisão de palavras e o loop será executado apenas uma vez. Portanto, use a sintaxe acima:
#!/bin/bash # make sure you always put $f in double quotes to avoid any nasty surprises i.e. "$f" for f in "$*" do echo "Processing $f file..." # rm "$f" done
Observe que $ @ expandido como “$ 1” “$ 2” “$ 3”… “$ n” e $ * expandido como “$ 1y $ 2y $ 3y… $ n”, onde y é o valor da variável IFS, ou seja, “$ * ”É uma string longa e $ IFS age como um separador ou delimitador de token.
Bash Shell Loop sobre um conjunto de arquivos
O exemplo a seguir usa variáveis de shell para armazenar nomes de caminho reais e, em seguida, os arquivos são processados usando o loop for:
#!/bin/bash _base="/jail/.conf" _dfiles="${_base}/nginx/etc/conf/*.conf" for f in $_dfiles do lb2file="/tmp/${f##*/}.$$" #tmp file sed 's/Load_Balancer-1/Load_Balancer-2/' "$f" > "${lb2file}" # update signature scp "${lb2file}" nginx@lb2.nixcraft.net.in:${f} # scp updated file to lb2 rm -f "${lb2file}" done
Resumindo
Você aprendeu a escrever ou configurar o bash para arquivos de loop-over. É fácil, mas deve-se tomar cuidado ao lidar com arquivos inexistentes ou arquivos com caracteres especiais, como espaços em branco. Para superar esses dois problemas, podemos combinar os comandos find e xargs da seguinte forma:
# Search all pdf files copy it safely # # Tested on GNU/Linux, macOS and FreeBSD (BSD/find/xargs) # find /dir/to/search -type f -name "*.pdf" -print0 | xargs -0 -I {} echo "Working on {}" find /dir/to/search -type f -name "*.pdf" -print0 | xargs -0 -I {} cp "{}" /dest/dir # # Find all "*.c" files in ~/project/ and move to /nas/oldproject/ dir # Note -iname is same as -name but the match is case insensitive. # find ~/projects/ -type f -iname "*.c" |\ xargs -0 -I {} mv -v "{}" /nas/oldprojects/
Onde encontrar opções de comando são:
Where find command options are:
- -type f : Only search files
- -name "*.pdf" OR -iname "*.c" : Search for all pdf files. The -iname option enables case insensitive match for all C files.
- -print0 : Useful to deal with spacial file names and xargs. It will print the full file name on the standard output (screen), followed by a null character (instead of the newline character that -print uses). This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corresponds to the -0 option of xargs.
The output of find command is piped out to the xargs command, and xargs options are:
- -0 : Input items are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every character is taken literally). Disables the end of file string, which is treated like any other argument. Useful when input items might contain white space, quote marks, or backslashes. The GNU find -print0 option produces input suitable for this mode.
- -I {} : Replace occurrences of {} in the initial-arguments with names read from standard input i.e. find command output.
- echo OR cp : Shell command to run over this file denoted by {}
See man pages:
man bash
help for
man find
man xargs
Exemplo que precisei para CSV
do
echo "Copy for csv file - $f"
# always "double quote" $f to avoid problems
curl -T /var/lib/docker/volumes/docker-postgresql11-replication_pg_data/_data/pg_log/"$f" -u admin:password "ftp://127.0.0.1:21/dumps/"$f""
#/path/to/command --option "$f"
done
Fonte:
- https://www.cyberciti.biz/faq/bash-loop-over-file/
Donate to Site
Renato
Developer