Usando find :
find . -type f -name '*_*.jpg' -exec sh -c '
for pathname do
newname=${pathname##*/}
newname="${pathname%/*}/${newname%%_*}.jpg"
printf "Would move %s to %s\n" "$pathname" "$newname"
# mv -i "$pathname" "$newname"
done' sh {} +
Isto encontraria os nomes de caminho todos os arquivos regulares dentro ou abaixo do diretório atual cujos nomes correspondem ao padrão dado. Para lotes desses nomes de caminho, é executado um pequeno script de shell que faz um loop sobre os nomes de caminho fornecidos e renomeia os arquivos (a real renomeação é comentada por segurança).
Dado um nome de caminho como ./a/b/c/foo0_some_other_bits.jpg , o script de shell transformaria isso em ./a/b/c/foo0.jpg primeiro excluindo os diretórios (produzindo foo0_some_other_bits.jpg ) e, em seguida, excluindo tudo após o primeiro caractere _ e adicionando os diretórios novamente. O bit de diretório do nome do caminho é excluído e substituído apenas no caso de conter um ou vários caracteres _ . Isso é feito usando expansões de parâmetros padrão.
Usando a opção globstar shell em bash :
shopt -s globstar # use "set -o globstar" in ksh93, or remove completely in zsh
for pathname in ./**/*.jpg; do
[ -f "$pathname" ] || continue
newname=${pathname##*/}
newname="${pathname%/*}/${newname%%_*}.jpg"
printf "Would move %s to %s\n" "$pathname" "$newname"
# mv -i "$pathname" "$newname"
done
Isso é exatamente equivalente ao comando find acima, com a única diferença de que ele não encontraria nomes ocultos (adicione shopt -s dotglob para isso).