Parseando paths e nomes problemáticos

Para começar essa série em que divido alguns truques maneiros de shell-scripting, vou mostrar uma solução elegante para scripts que precisam parsear pathnames e/ou nomes de arquivos com espaços, caracteres especiais, essas coisas problemáticas que não podem ser resolvidas com um mero

for VARIAVEL in … ;

para definir um loop,

sendo que em muitos casos read -r tbm não dá conta;

O script a seguir converteu o bit depth e sample rate das minhas 10k musicas FLAC sem um unico erro. Tô falando de uma estrutura de diretórios complexa, com nomes cheios de caracteres absurdos, como [, {, espaços e etc.

A idéia aqui é o conceito da aplicação do find, com xargs, e um subshell invocado espertamente. Bora?

Segurem a peruca!

1. Usando find com -print0

O comando find é usado para procurar arquivos. A opção -print0 faz com que os nomes dos arquivos sejam seguidos por um caractere nulo ao invés de uma nova linha. Isso é importante para lidar com nomes de arquivos que contêm espaços e caracteres especiais.

find "$1" -type f -iname "*.flac" -print0

2. Usando xargs com -0 e -I {}

O xargs pega a saída do find e a passa como argumentos para outro comando. A opção -0 diz ao xargs para esperar que os itens de entrada sejam terminados por nulos, correspondendo ao formato de saída do find … -print0.

A opção -I {} permite que você especifique um placeholder {} que será substituído pelo caminho do arquivo.

xargs -0 -I {} bash -c '...comandos...' _ {}

3. Executando bash -c com o Caminho do Arquivo

O comando bash -c é usado para executar uma string de comandos bash. Passando uma string de comandos para bash -c, você pode usar lógica complexa e manipulação de variáveis dentro da string.

bash -c '...comandos...' _ {}
  • O primeiro argumento após -c é a string de comandos a ser executada.
  • O segundo argumento _ é um argumento fictício que o bash ignora.
  • O terceiro argumento {} é passado como o primeiro parâmetro posicional $1 dentro da string bash -c.

4. Manipulando o Caminho do Arquivo Dentro do bash -c

Dentro da string bash -c, o caminho do arquivo é atribuído a uma variável file usando “$1”. Isso garante que o caminho do arquivo seja tratado corretamente, mesmo se contiver espaços ou caracteres especiais.

file="{}"

Exemplo Detalhado

Dado este comando:

xargs -0 -I {} bash -c 'file="${1}"; echo "$file"' _ {}

O placeholder {} é substituído pelo caminho do arquivo, e $1 dentro do bash -c recebe esse caminho do arquivo. Tanto file=”{}” quanto file=”${1}” definirão file para o caminho do arquivo.

Exemplo de Script

https://github.com/guprobr/shell-foo/blob/main/flac_24bit_48k.sh

Aqui está o script inteiro com o uso explícito de file=”${1}”:

#!/bin/bash

convert_to_24bit_48khz() {
find "$1" -type f -iname "*.flac" -print0 | xargs -0 -I {} bash -c '
file="${1}" # Assign the current file path to the variable "file"
# Check if the file is already 24-bit and 48kHz
bit_depth=$(ffmpeg -i "$file" 2>&1 | grep -oP "Stream.*Audio:.*?(\d+) Hz, \d+ channels, s(\d+)")
sample_rate=$(ffmpeg -i "$file" 2>&1 | grep -oP "Stream.*Audio:.*?(\d+) Hz, \d+ channels, s\d+" | grep -oP "(\d+) Hz")
if [[ $bit_depth =~ s24 && $sample_rate -eq 48000 ]]; then
echo "*************************Skipping \"$file\" (already 24-bit, 48kHz)*************************"
else
# Convert to 24-bit, 48kHz with maximum quality resampling and dithering
echo "---Converting \"$file\" to 24-bit, 48kHz"
if ffmpeg -y -i "$file" -c:a flac -sample_fmt s32 -ar 48000 -af "aresample=resampler=soxr:precision=28:dither_method=triangular" "${file%.flac}_24bit_48khz.flac"; then
mv -v "${file%.flac}_24bit_48khz.flac" "$file"
else
echo "FFMpeg failure, A B O R T i n g"; exit 1
fi
fi
' _ {}
}

# Starting point: current directory or specified directory
start_dir="${1:-.}"
convert_to_24bit_48khz "$start_dir"

Resumo

  • file=”{}” e file=”${1}” são funcionalmente iguais neste contexto porque {} é substituído por $1.
  • Usar file=”${1}” torna o script mais explícito e claro de que $1 é o primeiro parâmetro posicional passado para o comando bash -c.

Este método de manipulação de caminhos garante que seu script processe cada arquivo corretamente, mesmo que existam espaços ou caracteres especiais nos nomes dos arquivos.

Jóia galera? Aguardem por mais dicas maneiras! Não sou um mago do shell-script mas acredito que toda informação dividida, é conhecimento multiplicado! Vlw!!!!!