cp
과제를 위해 나는 함수 (복사) 와 동일한 기본 기능을 가진 bash 함수를 영리하게 작성하라는 요청을 받았습니다 . 하나의 파일을 다른 파일로 복사하기만 하면 되므로 여러 파일이 새 디렉터리에 복사되지 않습니다.
나는 bash 언어를 처음 접했기 때문에 내 프로그램이 작동하지 않는 이유를 이해할 수 없습니다. 원래 함수에서는 파일이 이미 있으면 덮어쓰도록 요청하므로 이를 구현해 보았습니다. 실패합니다.
파일이 여러 줄에서 실패하는 것처럼 보이지만 가장 중요한 것은 복사할 파일이 이미 존재하는지 확인하는 조건( [-e "$2"]
)입니다. 그럼에도 불구하고 해당 조건이 충족되면 트리거되어야 하는 메시지(파일 이름 ...)가 계속 표시됩니다.
누구든지 이 파일을 수정하는 데 도움을 주고 언어에 대한 기본적인 이해에 유용한 통찰력을 제공할 수 있습니까? 코드는 다음과 같습니다.
#!/bin/sh
echo "file variable: $2"
if [-e file]&> /dev/null
then
echo "The file name already exists, want to overwrite? (yes/no)"
read | tr "[A-Z]" "[a-z]"
if [$REPLY -eq "yes"] ; then
rm "$2"
echo $2 > "$2"
exit 0
else
exit 1
fi
else
cat $1 | $2
exit 0
fi
답변1
해당 파일이 이미 존재하는 경우 유틸리티 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
하다do ( 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"
}
이 함수는 "bashism"을 사용하지 않으며 모든 sh
-like 쉘에서 작동해야 합니다.
여러 소스 파일을 지원하기 위해 조금 더 조정하고 -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,,}"
(소문자의 경우)를 사용하는 것이 더 저렴한 방법이었을 것입니다.
사용자가 코드를 사용하여 "예"라고 대답하면 함수는 대상 파일의 파일 이름을 대상 파일에 넣습니다. 이것은 소스 파일을 복사하는 것이 아닙니다. 함수의 실제 복사 비트까지 전달되어야 합니다.
복사 비트는 파이프라인을 사용하여 구현한 것입니다. 파이프라인은 한 명령의 출력에서 다른 명령의 입력으로 데이터를 전달하는 데 사용됩니다. 이것은 우리가 여기서 해야 할 일이 아닙니다. 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
예를 들어 비정규 파일과 같은 경우와 상당히 다르게 작동한다는 것입니다.