I am writing a menu based bash script, one of the menu options is to send an email with a text file attachment. I am having trouble with checking if my file is a text file. Here is what I have:
fileExists=10
until [ $fileExists -eq 9 ]
do
echo "Please enter the name of the file you want to attach: "
read attachment
isFile=$(file $attachment | cut -d -f2)
if [[ $isFile = "ASCII" ]]
then
fileExists=0
else
echo "$attachment is not a text file, please use a different file"
fi
done
I keep getting the error cut: delimiter must be a single character.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
-
From the fact that it says
file $attachment
rather thanfile "$attachment",
I guess your script cannot handle filenames that contain spaces.
But, be advised that filenames can contain spaces,
and well-written scripts can handle them. Note, then:$ file "foo bar" foo bar: ASCII text $ file "foo bar" | cut -d' ' -f2 bar:
One popular and highly recommended approach
is to null-terminate the filenames:$ file -0 "foo bar" | cut -d $'' -f2 : ASCII text
-
The
filecommand makes educated guesses about
what type of file a file is. Guesses, naturally, are sometime wrong.
For example,filewill sometimes look at an ordinary text file
and guess that it is a shell script, C program, or something else.
So you don’t want to check whether the output fromfileisASCII text,
you want to see whether it says that the file is a text file.
If you look at the man page forfile,
you will see that it more-or-less promises
to include the wordtextin its output if the file is a text file,
but this might be in a context likeshell commands text.
So, it may be better to check whether the output fromfile
contains the wordtext:isFile=$(file -0 "$attachment" | cut -d $'' -f2) case "$isFile" in (*text*) echo "$attachment is a text file" ;; (*) echo "$attachment is not a text file, please use a different file" ;; esac
Method 2
The problem occurs in cut -d -f2. Change it to cut -d -f2.
To cut, the arguments look like this:
# bash: args(){ for i; do printf '%q \n' "$i"; done; }
# args cut -d -f2
cut
-d -f2
And here is the problem. escaped the space to a space literal instead of a delimiter between arguments in your shell, and you didn’t add an extra space so the whole -d -f2 part appears as one argument. You should add one extra space so -d and -f2 appear as two arguments.
To avoid confusion, many people use quotes like -d' ' instead.
P.S.: Instead of using file and making everything ASCII, I’d rather use
if file "$attachment2" | grep -q text$; then
# is text
else
# file doesn't think it's text
fi
Method 3
I would circumvent the escaping and do:
... | cut -d' ' -f2
that way it is clear that you need a space between the delimiter character (specified by the three letters sequence ' ') and the following option. With -d -f2 it is easy to miss you should have done -d -f2.
Method 4
case $(file -b --mime-type - < "$attachment") in
(text/*)
printf '%sn' "$attachment is probably text according to file"
case $(file -b --mime-encoding - < "$attachment") in
(us-ascii) echo "and probably in ASCII encoding"
esac
esac
Method 5
Another option is to not use cut and to match a regex against the full output of file:
#... isFile=$(file $attachment) if [[ "$var" =~ ^[^:]*: ASCII ]] #...
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0