Estou tentando escrever um script em zsh (Linux Mint 21.3 "Virginia") para mover (ou copiar) um arquivo de $source para $target com resultados confusos. Estou usando caixas de diálogo de arquivo yad para selecionar os arquivos e nomear o diretório de destino. O melhor que consegui fazer foi copiar apenas a escolha final para o destino vazio.
Aqui está o roteiro completo
setopt -x
setopt -v
autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
echo $array
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
echo "target="$target
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
exit 0
Aqui está a saída do terminal de duas execuções do script usando os mesmos parâmetros.
+/home/revision-6/bin/cpi:4> setopt -v
autoload -Uz zmv
+/home/revision-6/bin/cpi:5> autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
+/home/revision-6/bin/cpi:11> declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
+/home/revision-6/bin/cpi:12> array=+/home/revision-6/bin/cpi:12> yad '--width=600' '--height=800' '--title=Choose files to move.' --file --multiple '--separator=\n'
(yad:3962282): dbind-WARNING **: 15:34:42.076: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:12> array=$'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
echo $array
+/home/revision-6/bin/cpi:15> echo $'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
+/home/revision-6/bin/cpi:17> target=+/home/revision-6/bin/cpi:17> yad '--width=600' '--height=800' '--title=Choose destination.' --file --directory
(yad:3962312): dbind-WARNING **: 15:34:54.949: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:17> target=/home/revision-6/video_processing
echo "target="$target
+/home/revision-6/bin/cpi:20> echo 'target=/home/revision-6/video_processing'
target=/home/revision-6/video_processing
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
+/home/revision-6/bin/cpi:38> filename=/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:39> unset x
+/home/revision-6/bin/cpi:42> filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:43> echo
+/home/revision-6/bin/cpi:44> echo
+/home/revision-6/bin/cpi:45> echo 'filename=betty_boop_more_pep_1936.mkv'
filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:46> echo
+/home/revision-6/bin/cpi:47> echo
+/home/revision-6/bin/cpi:49> shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:50> echo 'shortname=betty_boop_more_pep_1936'
shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:51> echo
+/home/revision-6/bin/cpi:52> echo
+/home/revision-6/bin/cpi:54> ext=mkv
+/home/revision-6/bin/cpi:55> echo
+/home/revision-6/bin/cpi:56> echo
+/home/revision-6/bin/cpi:57> echo 'ext=mkv'
ext=mkv
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> find /home/revision-6/video_processing -iname 'betty_boop_more_pep_1936*.mkv'
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> wc -c
+/home/revision-6/bin/cpi:62> x=0
+/home/revision-6/bin/cpi:63> echo
+/home/revision-6/bin/cpi:64> echo
+/home/revision-6/bin/cpi:65> echo 'x=0'
x=0
+/home/revision-6/bin/cpi:73> case 0 (0)
+/home/revision-6/bin/cpi:74> cp betty_boop_more_pep_1936.mkv /home/revision-6/video_processing
+/home/revision-6/bin/cpi:75> sleep .2
exit 0
+/home/revision-6/bin/cpi:83> exit 0
revision-6@revision6-NYI3 ~/Videos
% cpi
+/home/revision-6/bin/cpi:4> setopt -v
autoload -Uz zmv
+/home/revision-6/bin/cpi:5> autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
+/home/revision-6/bin/cpi:11> declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
+/home/revision-6/bin/cpi:12> array=+/home/revision-6/bin/cpi:12> yad '--width=600' '--height=800' '--title=Choose files to move.' --file --multiple '--separator=\n'
(yad:3962420): dbind-WARNING **: 15:35:35.029: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:12> array=$'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
echo $array
+/home/revision-6/bin/cpi:15> echo $'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
+/home/revision-6/bin/cpi:17> target=+/home/revision-6/bin/cpi:17> yad '--width=600' '--height=800' '--title=Choose destination.' --file --directory
(yad:3962450): dbind-WARNING **: 15:35:43.654: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:17> target=/home/revision-6/video_processing
echo "target="$target
+/home/revision-6/bin/cpi:20> echo 'target=/home/revision-6/video_processing'
target=/home/revision-6/video_processing
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
+/home/revision-6/bin/cpi:38> filename=/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:39> unset x
+/home/revision-6/bin/cpi:42> filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:43> echo
+/home/revision-6/bin/cpi:44> echo
+/home/revision-6/bin/cpi:45> echo 'filename=betty_boop_more_pep_1936.mkv'
filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:46> echo
+/home/revision-6/bin/cpi:47> echo
+/home/revision-6/bin/cpi:49> shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:50> echo 'shortname=betty_boop_more_pep_1936'
shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:51> echo
+/home/revision-6/bin/cpi:52> echo
+/home/revision-6/bin/cpi:54> ext=mkv
+/home/revision-6/bin/cpi:55> echo
+/home/revision-6/bin/cpi:56> echo
+/home/revision-6/bin/cpi:57> echo 'ext=mkv'
ext=mkv
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> find /home/revision-6/video_processing -iname 'betty_boop_more_pep_1936*.mkv'
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> wc -c
+/home/revision-6/bin/cpi:62> x=63
+/home/revision-6/bin/cpi:63> echo
+/home/revision-6/bin/cpi:64> echo
+/home/revision-6/bin/cpi:65> echo 'x=63'
x=63
+/home/revision-6/bin/cpi:73> case 63 (0)
+/home/revision-6/bin/cpi:73> case 63 (*)
+/home/revision-6/bin/cpi:77> mv betty_boop_more_pep_1936.mkv 63.mkv
+/home/revision-6/bin/cpi:78> cp betty_boop_more_pep_1936.mkv /home/revision-6/video_processing
cp: cannot stat 'betty_boop_more_pep_1936.mkv': No such file or directory
+/home/revision-6/bin/cpi:79> sleep .2
exit 0
+/home/revision-6/bin/cpi:83> exit 0
Para mim a lógica parece válida. Conte o número de iterações no destino e renomeie o arquivo ANTES de movê-lo, mas acho que meu shell-scripting-fu não está à altura da tarefa. Qualquer ajuda é bem vinda.
Isso pode ser mais fácil com
zsh
a poderosa sintaxe globalfind
em vez de ewc
. Experimente isto:A declaração principal é esta:
Ele cria uma matriz classificada de nomes de arquivos do diretório de destino que corresponde ao padrão baseado no nome do arquivo de origem.
${fqp:r}
- a raiz do caminho do arquivo. Contém o diretório e a parte inicial do nome, ou seja, tudo menos a extensão.(_copy<->|)
-_copyN
, ou nada (por exemplo,fname.ext
oufname_copy14.ext
).<->
é um padrão glob para qualquer número.${fqp:e}
- a extensão do nome do arquivo.(#qNn)
- um conjunto de qualificadores globais que modificam o comportamento#q
- indica que esta expressão entre parênteses contém qualificadores glob.N
- permitir glob nulo, para que zero correspondências não sejam um erro.n
- ative NUMERIC_GLOB_SUBST para esta expansão, para que os resultados sejam ordenados numericamente. Com esta opção, um conjunto de nomes comof1
f11
f2
será classificado comof1
f2
f11
. Nomes de arquivos sem dígitos no mesmo lugar, comofname.ext
efname_copy1.ext
ainda serão classificados lexicalmente, mas aqui essa é a ordem que queremos, portanto, nenhum tratamento especial é necessário.Com o array classificado, o último valor do array (index
-1
) será o nome do arquivo com o número mais alto, então podemos usar isso para determinar o que deve acontecer a seguir. Uma vantagem de usar essa técnica é que ela pode lidar com lacunas na numeração; um algoritmo baseado em contagem pode ser prejudicado por isso.Usando/testando:
Podemos usar esta função para construir um script que percorre uma matriz de nomes de arquivos: