cpУтилита счастливо перезаписывать целевой файл , если этот файл уже существует, без запроса пользователя.
Функция, которая реализует основные cpвозможности, без использования cpбудет
cp () {
cat "$1" >"$2"
}
Если вы хотите попросить пользователя перед перезаписью цели (обратите внимание, что это может быть нежелательно, если функция вызывается неинтерактивной оболочкой):
cp () {
if [ -e "$2" ]; then
printf '"%s" exists, overwrite (y/n): ' "$2" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$2"
}
Диагностические сообщения должны идти в стандартный поток ошибок. Это то, что я делаю printf ... >&2.
Обратите внимание, что нам на самом деле не нужен rmцелевой файл, поскольку перенаправление будет его урезать. Если бы мы сначала захотели rm, то вам нужно было бы проверить, является ли это каталогом, и если это так, вместо этого поместите целевой файл в этот каталог, как это и cpбудет сделано. Это делает это, но все еще без явного rm:
cp () {
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
Возможно, вы также захотите убедиться, что источник действительно существует, что cp делает что-то ( catделает это тоже, поэтому, конечно, его можно полностью исключить, но при этом будет создан пустой целевой файл):
cp () {
if [ ! -f "$1" ]; then
printf '"%s": no such file\n' "$1" >&2
return 1
fi
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
Эта функция не использует "bashisms" и должна работать во всех shподобных оболочках.
Немного больше настроек для поддержки нескольких исходных файлов и -iфлаг, который активирует интерактивные подсказки при перезаписи существующего файла:
cp () {
local interactive=0
# Handle the optional -i flag
case "$1" in
-i) interactive=1
shift ;;
esac
# All command line arguments (not -i)
local -a argv=( "$@" )
# The target is at the end of argv, pull it off from there
local target="${argv[-1]}"
unset argv[-1]
# Get the source file names
local -a sources=( "${argv[@]}" )
for source in "${sources[@]}"; do
# Skip source files that do not exist
if [ ! -f "$source" ]; then
printf '"%s": no such file\n' "$source" >&2
continue
fi
local _target="$target"
if [ -d "$_target" ]; then
# Target is a directory, put file inside
_target="$_target/$source"
elif (( ${#sources[@]} > 1 )); then
# More than one source, target needs to be a directory
printf '"%s": not a directory\n' "$target" >&2
return 1
fi
if [ -d "$_target" ]; then
# Target can not be overwritten, is directory
printf '"%s": is a directory\n' "$_target" >&2
continue
fi
if [ "$source" -ef "$_target" ]; then
printf '"%s" and "%s" are the same file\n' "$source" "$_target" >&2
continue
fi
if [ -e "$_target" ] && (( interactive )); then
# Prompt user for overwriting target file
printf '"%s" exists, overwrite (y/n): ' "$_target" >&2
read
case "$REPLY" in
n*|N*) continue ;;
esac
fi
cat -- "$source" >"$_target"
done
}
В вашем коде плохие интервалы if [ ... ](нужно пространство до и после [, и до ]). Вы также не должны пытаться перенаправить тест, /dev/nullпоскольку сам тест не имеет выходных данных. Кроме того, первый тест должен использовать позиционный параметр $2, а не строку file.
Используя, case ... esacкак я, вы избежите необходимости использовать строчные / прописные буквы ответа пользователя tr. В bash, если бы вы хотели сделать это в любом случае, более дешевым способом сделать это был бы использование REPLY="${REPLY^^}"(для верхнего регистра) или REPLY="${REPLY,,}"(для lowercasing).
Если пользователь говорит «да» с вашим кодом, функция помещает имя файла целевого файла в целевой файл. Это не копирование исходного файла. Это должно провалиться до фактического бита копирования функции.
Бит копирования - это то, что вы реализовали с помощью конвейера. Конвейер используется для передачи данных с выхода одной команды на вход другой команды. Это не то, что нам нужно делать здесь. Просто вызовите catисходный файл и перенаправьте его вывод в целевой файл.
То же самое не так с твоим призывом trранее. readбудет устанавливать значение переменной, но не будет выводить данные, так readчто передача чего-либо не имеет смысла.
Никакого явного выхода не требуется, если пользователь не говорит «нет» (или функция сталкивается с некоторым условием ошибки, как в битах моего кода, но так как это функция, которую я использую, returnа не exit).
Кроме того, вы сказали «функция», но ваша реализация представляет собой скрипт.
Загляните на https://www.shellcheck.net/ , это хороший инструмент для выявления проблемных фрагментов скриптов оболочки.
Использование cat- это только один из способов скопировать содержимое файла. Другие способы включают
dd if="$1" of="$2" 2>/dev/null
- Использование любой подобной фильтру утилиты, которую можно заставить просто передавать данные, например
sed "" "$1" >"2"или awk '1' "$1" >"$2"или tr '.' '.' <"$1" >"$2"и т. Д.
- и т.п.
Сложность заключается в том, чтобы заставить функцию копировать метаданные (владение и разрешения) из источника в цель.
Еще одна вещь, на которую следует обратить внимание: написанная мной функция будет вести себя совсем не так, как cpесли бы целью было что-то вроде, /dev/ttyнапример (нестандартный файл).