For an assignment I am asked to cleverly write a bash function which has the same basic functionality as the function cp
(copy). It only has to copy one file to another, so no multiple files copied to a new directory.
Since I am new to the bash language, I can't understand why my program is not working. The original function asks to overwrite a file if it is already present, so I tried to implement that. It fails.
The file fails at multiple lines it seems, but most importantly at the condition where it checks if the file to be copied to already exists ([-e "$2"]
). Even so, it still shows the message that is supposed to be triggered if that condition is met (The file name …).
Could anyone help me in fixing this file, possibly providing some useful insight in my basic comprehension of the language?
The code is as follows.
#!/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
Best Answer
The
cp
utility will happily overwrite the target file if that file already exists, without prompting the user.A function that implements basic
cp
capability, without usingcp
would beIf you want to prompt the user before overwriting the target (note that it may not be desireable to do this if the function is called by a non-interactive shell):
The diagnostic messages should go to the standard error stream. This is what I do with
printf ... >&2
.Notice that we don't really need to
rm
the target file as the redirection will truncate it. If we did want torm
it first, then you'd have to check whether it's a directory, and if it is, put the target file inside that directory instead, just the waycp
would do. This is doing that, but still without explicitrm
:You may also want to make sure that the source actually exists, which is something
cp
does do (cat
does it too, so it may be left out completely, of course, but doing so would create an empty target file):This function uses no "bashisms" and should work in all
sh
-like shells.With a little bit more tweaking to support multiple source files and a
-i
flag that activates the interactive prompting when overwriting an existing file:Your code has bad spacings in
if [ ... ]
(need space before and after[
, and before]
). You also shouldn't try redirecting the test to/dev/null
as the test itself has no output. The first test should furthermore use the positional parameter$2
, not the stringfile
.Using
case ... esac
as I did, you avoid having to lowercase/uppercase the response from the user usingtr
. Inbash
, if you had wanted to do this anyway, a cheaper way of doing it would have been to useREPLY="${REPLY^^}"
(for uppercasing) orREPLY="${REPLY,,}"
(for lowercasing).If the user says "yes", with your code, the function puts the filename of the target file into the target file. This is not a copying of the source file. It should fall through to the actual copying bit of the function.
The copying bit is something you've implemented using a pipeline. A pipeline is used to pass data from the output of one command to the input of another command. This is not something we need to do here. Simply invoke
cat
on the source file and redirect its output to the target file.The same thing is wrong with you calling of
tr
earlier.read
will set the value of a variable, but produces no output, so pipingread
to anything is nonsensical.No explicit exit is needed unless the user says "no" (or the function comes across some error condition as in bits of my code, but since it's a function I use
return
rather thanexit
).Also, you said "function", but your implementation is a script.
Do have a look at https://www.shellcheck.net/, it's a good tool for identifying problematic bits of shell scripts.
Using
cat
is just one way to copy the contents of a file. Other ways includedd if="$1" of="$2" 2>/dev/null
sed "" "$1" >"2"
orawk '1' "$1" >"$2"
ortr '.' '.' <"$1" >"$2"
etc.The tricky bit is to make the function copy the metadata (ownership and permissions) from source to target.
Another thing to notice is that the function I've written will behave quite differently from
cp
if the target is something like/dev/tty
for example (a non-regular file).