Рокировка имен файлов в Linux

Часто ли вам приходилось менять местами названия двух файлов? Внутри я предлагаю вашему вниманию скрипт, который позволяет делать это одной командой, а не 3мя.

Конечно, для большинства эта задача не кажется такой уж трудоемкой, однако лично мне было бы приятней делать это одной командой, а не тремя. Специально для этой задачи я написал небольшой bash-скрипт «cas»(от castling (англ.) — рокировка) и решил с вами им поделиться. Чтобы пользоваться им на своей системе, вы должны сохранить содержимое листинга в файл(например, cas), поместить его, скажем в /usr/bin(или любой другой путь, находящийся в $PATH) и сделать его исполняемым chmod a+x /usr/bin/cas. Как пользоваться им, думаю, проблем не возникнет.
Скрипт делался для себя и по большей части из-за интереса и от «нечего делать», однако конструктивная критика, замечания, пожелания и т.п. приветствуются.

Теперь листинг самого скрипта:

#!/bin/bash

#В этой переменной будет имя и путь для первого файла
what=$1 
#В этой для второго
for_what=$2
#путь к временному каталогу
TMP=/tmp

#рандомная строка, которая будет добавляться к временному имени файла
random_sybm=$(cat /dev/urandom| tr -dc 'a-zA-Z0-9' | fold -w 10| head -n 1) 

#функция вывода текста, если программа запущена без параметров
usage() {
  echo "Usage: $0 1ST_FILE 2ND_FILE
        Try 'cas --help' for more information."
  exit
}

#функция вывода справки
show_help() {
echo "Usage: $0 1ST_FILE 2ND_FILE
Changes the file name 1ST_FILE to 2ND_FILE and 2ND_FILE to 1ST_FILE.
Available options:
        --help          Display this help and exit.
        --version       Output version information and exit.
"
exit
}

#функция вывода версии скрипта 
show_version() {
echo "cas 0.11

This is free software.  You may redistribute copies of it under the terms of
the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
There is NO WARRANTY, to the extent permitted by law.

Written by Mikhail M, mihail.ite@gmail.com.
"
exit
}

#Функция, которая выполняет переименование/перемещение файлов
do_castling_file() {
mv $what $TMP/castling_$random_sybm
mv $for_what $what
mv $TMP/castling_$random_sybm $for_what
}

#Цикл для обработки аргументов командной строки
for i in $*; do
if [[ $i == "--help" || $i == "-h" ]]; then 
show_help
fi
if [[ $i == "--version" || $i == "-v" ]]; then
show_version
fi
done

#Если количество параметров < 2, то вывести справочную информацию о том как использовать скрипт
if [ $# -ne 2 ]; then
usage
fi

#Проверяем имеем ли необходимые привилегии на чтение файлов
if [ -r $what ]; then
 if [ -r $for_what ]; then
#А на запись?
        if [ -w $what ]; then
         if [ -w $for_what ]; then
#Если все ОК, то вызываем функцию перемещения
         do_castling_file
         else
#Выводим ошибку, если не имеем прав на запись в файл
         echo "No permission to write a file $for_what"
         exit 2
         fi
        else echo "No permission to write a file $what"
        exit 2
        fi
 else
#Выводим ошибку, если не имеем прав на чтение из файла
 echo "File $for_what doesin't exist or you haven't permissions for reading"
 exit 2
 fi
else
echo "File $what doesin't exist or you haven't permissions for reading"
exit 2
fi

#Если выполнение скрипта дошло до сюда, то выходим и возвращаем код ошибки 0
exit 0 
–25
21 февраля 2012, 16:53
8
ite 17,5

комментарии (27)

+4
augur #
Вы не учитываете такой вариант, когда временная папка находится на другом разделе диска, а размеры переименовываемых файлов измеряются гигабайтами.
+1
ite #
Да, действительно это не учтено, не приходилось применять скрипт на больших файлов, использую в основном его для переименования конфигов.
+2
ToSHiC #
В локальной директории команда mv не копирует данные, а только так изменяет директорию, чтобы в ней на иноду файла указывало новое имя. Если делать mv 3 раза — то будет 0 копирований файла и 3 изменения директории, которые ещё и будут в pagecache находиться, т.е. реально будет выполнено 1 чтение с диска и потом, когда нибудь, случится ещё 1 запись.
/tmp в большинстве систем находится на другой файловой системе, так что при выполнении скрипта файл будет копироваться аж 2 раза, и даже если /tmp находится в памяти, будет 1 чтение всего файла и, вероятно, его запись. Ну и чтение директории, естественно.
+10
fleshy #
для генерации случайного имени есть mktemp
0
alexxz #
Вобщем как-то неправильно вы подошли к задаче. Не учтено, что файлы могут быть большими. Что в /tmp может быть другой раздел. Зато проверяли достпупность на запись и прочее, как будто вы пишите программу не на bash а, на Си.

Вот бы увидеть уличную магию, когда два больших файла находятся на разных партициях, при этом на этих партициях нет свободного места.
–1
ite #
Метод решения задачи был выбран исходя из текущей надобности, а именно: быстрое переименование конф. файлов, находящихся в одной директории. Разумеется, при переименовании больших файлов находящихся на разных партициях должен быть выбран иной подход. Кстати, у Вас есть идея как сделать такое(большие файлы, разные разделы и т.п.) переименование более правильным?
+1
redrampage #
mv $what $TMP/castling_$random_sybm
mv $for_what $what
mv $TMP/castling_$random_sybm $for_what
rm -f $TMP/castling_$random_sybm


Это чтобы наверняка добить не перемещенный по какой-либо причине первый файл?
По хорошему надо проверять код выхода каждого mv, и если что-то пошло не так бить тревогу.
0
ite #
уже исправил)
+21
muhas #
if [ -f "$2" ] && [ -f "$1" ] 
then
ln $1 $2.bak
mv $2 $1
mv $2.bak $1
fi

0
silvansky #
А вот это — *nix-way!
0
Alukardd #
Жёсткие ссылки работают только в пределах одного раздела! Даже не жёсткого диска, а именно раздела!!!
–3
ite #
вы уверены что эта конструкция будет работать?
–1
muhas #
да. проверил — работает. только для mv добавить параметр что бы не подтверждать mv $2 $1
ну и само собой файлуха должна жесткие ссылки поддерживать

хотя так же ещё есть ограничения — но они и в исходном скрипте тоже имеются, так что на них я внимание не обращал
0
ite #
а эти строчки вас не смущают?
mv $2 $1
mv $2.bak $1

в первой строке вы перемещаете файл2 в файл1( предварительно слинковав файл1 и файл2.bak), а после заменяете файл1 файлом2.bak, в итоге получается остается только файл 1, т.к. файл2 мы переименовали в файл1, а после затерли его файл2.bak
+1
muhas #
каюсь, грешен. mv $2.bak $2 конечно же
+1
redrampage #
Я бы даже перефразировал
if [ -f "$2" ] && [ -f "$1" ] 
then
ln $1 $2.bak && \
mv $2 $1 && \
mv $2.bak $1 || \
(echo "Error" && exit 1)
fi
0
lxyd #
И кавычки! Почему все забывают про кавычки?
Такое будет работать… эм… плохо :)
mv Текстовый документ.txt Текстовый документ 2.txt


Так что позволю себе эту версию чуть поправить:
if [ -f "$2" ] && [ -f "$1" ] 
then
ln "$1" "$2.bak" && \
mv "$2" "$1" && \
mv "$2.bak" "$1" || \
(echo "Error" && exit 1)
fi


Ну и уже к основной статье, чтобы не плодить комментов:
Для множества файлов лучше использовать не $*, а "$@" по той же причине. Эта конструкция корректно обрабатывает пробелы в аргументах, оборачивая каждый отдельно в кавычки.
+3
Lockal #
Ещё можно в man mv найти
--backup[=CONTROL], -b
              make a backup of each existing destination file
--suffix=SUFFIX, -S
              override the usual backup suffix

… и избавиться от вызова ln.
0
Veshij #
имя темпового файла получать проще так:
mktemp -q /tmp/XXXXXX
0
ite #
Для тех, кто перемещает большие файлы в пределах одной партиции, функцию можно поменять на:
do_castling_file() {
mv $what .$what_$random_sybm
mv $for_what $what
mv .$what_$random_sybm $for_what
}
0
ite #
простите, это не вам, это должен был быть камент к топику.
0
Lennier #
Про mktemp товарищь выше верно подметил. Ещё почитайте про getopts, а то циклы пиать для обрабоки опций как то пионеристо.
0
gvsmirnov #
CAS уже занято под Compare And Swap, которое не имеет никакого отношения к тому, что вы делаете. Назовите просто swap, всем понятнее будет. Если кто-то этим будет пользоваться, конечно :)
0
ite #
Мне кажется название swap еще больше введет в неведение пользователя, который это будет(если будет) использовать) Да и я не петендую на какое-то название, называйте как хотите, я лишь просто хотел поделиться тем, что упростило мне работу
0
gvsmirnov #
Как, простите, swap «введёт в неведение» больше, чем cas?
0
bliznezz #
Вам надо проверять права не на чтение-запись файла, а каталога в котором он лежит.
0
bliznezz #
Лично мне в скрипте резервного копирования приходится менять местами каталоги, револьвер на 2 посадочных места:

echo -n "`date` rotate previous dump to old: " >> dump.log
( mv current_tmp && mv old current && mv current_tmp old && echo OK >> dump_db.log ) || echo FAIL >> dump.log 2>&1

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.