Потребовалось заменить кусок имени файла во всех файлах
рекурсивно в директории. Намучавшись с файлами в имени
которых есть пробелы и спец символы, я сдался и в целях
экономии времени написал на том на чем смог. Теперь в целях
повышения образованности мне интересно узнать как это
правильно сделать с помощью bash, sed, mv, и возможно find. Для файлов без пробелов на bash я написал. С пробелами не справился и написал это:
import org.apache.commons.io.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
public class Replace {
public static void main(String[] args) {
List<File> files = (List) FileUtils.listFiles(new File("/home/zort/something"), null, true);
Pattern p = Pattern.compile("old");
for (File a : files) {
Matcher m = p.matcher(a.toString());
String asdf = m.replaceAll("new");
a.renameTo(new File(asdf));
System.out.println(asdf);
}
}
}
просветите меня.
ЗЫ знаю что ламер - у меня на капитальное изучение баша
времени не нашлось.
если подскажешь как после exec иди xargs аргумент разделить на переменные, и одну из неих отsedить, а потом передать мv, то я попробую. делать "man комманда" я умею, я в самом баше не разбираюсь.
Грязный хак, но в большенстве случаев работает :)
#!/bin/bash
for FILE_NAME in $(find . -type f | sed 's/ /SpAcE/g')
do
OLD_NAME=$(echo $FILE_NAME | sed s'/SpAcE/\ /g')
NEW_NAME=$(echo $OLD_NAME | sed 's/posix/shmozix/g')
mv "$OLD_NAME" "$NEW_NAME"
done
Запускай "на посмотреть":
find . -type f | sed -e 's/^\(.*\)posix\(.*\)$/mv "&" "\1shmozix\2"/'
Если все ОК добавь в конце | sh
find . -type f | sed -e 's/^\(.*\)posix\(.*\)$/mv "&" "\1shmozix\2"/' | sh
btw rename это perl скрипт, а хочется узнать как это делать башем и стандартными утилитами, ведь не только ренейминг делать приходится, а файлов с паршивыми именами меньше не становится. И в конце концов, возникает вопрос - "а не сакс ли баш вместе с пайпами?" и "не проще ли сразу не перле питоне или джаве писать ?". я всегда говорил что сакс, но сделать можно всё, если знаешь как. и сидя в линуксе я всегда пытаюсь сначала баш скрипт написать. поэтому хочется узнать.
Я бы сделал так:
[tmp]> ls -1
aa
aa posixbb q
'posixq w
z's" posix'" q
[tmp]> oldIFS="$IFS"
[tmp]> IFS=' <-- здесь я нажал Enter сразу после кавычки
> ' <-- а здесь ввёл кавычку и нажал Enter; > - это $PS2
[tmp]> for f in `find -type f`; do mv -v "$f" "`sed 's,posix,shmozix,' <<< "$f"`"; done
`./aa posixbb q' -> `./aa shmozixbb q'
mv: `./aa' и `./aa' - один и тот же файл
`./\'posixq w' -> `./\'shmozixq w'
`./z\'s" posix\'" q' -> `./z\'s" shmozix\'" q'
[tmp]> ls -1
aa
aa shmozixbb q
'shmozixq w
z's" shmozix'" q
[tmp]> IFS="$oldIFS"
[tmp]> unset oldIFS
Пожалуйста:
#!/bin/bash
IFS='
'
for f in `find "$3" -type f`; do
mv -v "$f" "`sed "s,$1,$2," <<< "$f"`"
done
Первый параметр - что заменить, второй - на что заменить, третий - где заменить.
Стандартные утилиты, баш, скобки и пробелы обрабатываются корректно:
#!/bin/bash
for FILE_NAME in $(find . -type f | sed 's/ /%20/g; s/(/%28/g; s/)/%29/g')
do
OLD_NAME=$(echo $FILE_NAME | sed 's/%20/ /g; s/%28/(/g; s/%29/)/g')
NEW_NAME=$(echo $OLD_NAME | sed 's/language/язык/g')
mv "$OLD_NAME" "$NEW_NAME"
done
Что ещё?
> Пока все работает. Дай пример файла, на котором мой "find | sed | sh" валится.
[tmp2]> ls -1
a"bebeqq
[tmp2]> ls -1 | sed 's/^\(.*\)bebe\(.*\)$/mv -v "&" "\1ihuh\2"/' | sh
mv: после `abebeqq aihuhqq' пропущен операнд, задающий целевой файл
Попробуйте `mv --help' для получения более подробного описания.
[tmp2]> ls -1 | sed 's/^\(.*\)bebe\(.*\)$/mv -v "&" "\1ihuh\2"/'
mv -v "a"bebeqq" "a"ihuhqq"
[tmp2]> cat ../rename.sh
#!/bin/bash
IFS='
'
for f in `find "$3" -type f`; do
mv -v "$f" "`sed "s,$1,$2," <<< "$f"`"
done
[tmp2]> ../rename.sh bebe ihuh .
`./a"bebeqq' -> `./a"ihuhqq'
[tmp2]> ls
a"ihuhqq
не разбирался из-за чего, но у меня на таких файлах как "CCCCCC,CCCCCCBB Sethi, !"№;"№;"№:№:%?*()(*?Ёёё`` ```$Ul``lman. Compilers.. principles, techniques, and tools (AW, 1986)(100dpi)(T)(811s)_S_" не сработало. я ужесточил задание. у mantes сработало.
Правильный вариант -- не писать на баше (если только это не сделанно специально), там где есть более одной интерпритации/подстановки строки (из-за возможных спецсимволов).
И это не потому что баш плохой, это особенности вложенных интерпритаций/подстановки.
На perl, в программе rename например, переменные не раскрываются более одного раза (если только это не сделанно специально).
[tmp2]> ls
a`ihuhqq
[tmp2]> ../rename.sh ihuh uhih .
`./a`ihuhqq' -> `./a`uhihqq'
[tmp2]> ls
a`uhihqq
У меня сработало. И с той большой строкой тоже. Хотя там форум часть символов попортил...
не знаю, может быть я что то испортил. посмотри.
zort@lambda:~/asd/rewq$ ls
BBBBB BBBBBB`BBBB
zort@lambda:~/asd/rewq$ cat ../fdsa
#!/bin/bash
asdfasdf="BBBB"
fdsafdsa="DDDD"
IFS='
'
for f in `find "$3" -type f`; do
mv -v "$f" "`sed "s,$asdfasdf,$fdsafdsa," <<< "$f"`"
done
zort@lambda:~/asd/rewq$ cat ../asdf
#!/bin/bash
for FILE_NAME in $(find . -type f | sed 's/ /%20/g; s/(/%28/g; s/)/%29/g')
do
OLD_NAME=$(echo $FILE_NAME | sed 's/%20/ /g; s/%28/(/g; s/%29/)/g')
NEW_NAME=$(echo $OLD_NAME | sed 's/BBBBB/CCCCCC/g')
mv "$OLD_NAME" "$NEW_NAME"
done
zort@lambda:~/asd/rewq$ ./../fdsa
find: invalid predicate `'
zort@lambda:~/asd/rewq$ ls -1
BBBBB
BBBBBB`BBBB
zort@lambda:~/asd/rewq$ ./../asdf
zort@lambda:~/asd/rewq$ ls -1
CCCCCC
CCCCCCB`BBBB
> Правильный вариант -- не писать на баше (если только это не сделанно специально), там где есть более одной интерпритации/подстановки строки (из-за возможных спецсимволов).
Вот я ещё буду на каждый чих перл привлекать :) Мне как-то хватало башевской защиты от спецсимволов.
> На perl, в программе rename например, переменные не раскрываются более одного раза (если только это не сделанно специально).
Если так важна скорость, то, может быть, баш и не лучший вариант. Но для повседневных задач это вполне достаточный инструмент для работы с файловой системой.
> не знаю, может быть я что то испортил. посмотри.
У тебя получилось так, что в моём скрипте $3 не определён. В моих примерах там стояла точка, что значит "текущая директория". Замени $3 на . или на путь, где лежат файлы, которые надо переименовать.
у меня стабильно на ` ругается.
скрипт:
#!/bin/bash
IFS='
'
for f in `find "$3" -type f`; do
mv -v "$f" "`sed "s,$1,$2," <<< "$f"`"
done
версии софта:
GNU bash, version 3.1.17(1)-release (i486-pc-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
zort@lambda:~/asd/rewq$ find --version
GNU find version 4.2.28
Features enabled: D_TYPE O_NOFOLLOW(enabled) LEAF_OPTIMISATION
zort@lambda:~/asd/rewq$ sed --version
GNU sed version 4.1.5
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
to the extent permitted by law.