Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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 I have tried adjusting spaces, I'm new to bash and those keep getting me. I'm also new to this site, so sorry if my code formatting doesn't look quite right.

share|improve this question

put on hold as off-topic by Michael Homer, G-Man, roaima, Stephen Kitt, PersianGulf 19 hours ago

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions describing a problem that can't be reproduced and seemingly went away on its own (or went away when a typo was fixed) are off-topic as they are unlikely to help future readers." – Michael Homer, G-Man, roaima, Stephen Kitt, PersianGulf
If this question can be reworded to fit the rules in the help center, please edit the question.

3  
Put an extra space after -d\ . –  Michael Homer yesterday
1  
Depending on the file version you have available you should consider using some options like --brief (which doesn't output the filename so you will have less of a problem with filenames that contain spaces) or --mime which returns the MIME type (e.g. text/plain) instead of a textual description of the file type. –  Dubu yesterday

5 Answers 5

up vote 1 down vote accepted

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
share|improve this answer
    
ah thank you! It was my spaces again, but it's working now! –  Powea 16 hours ago
    
@Powea Added an alternative. –  Arthur2e5 13 mins ago
  1. From the fact that it says file $attachment rather than file "$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 $'\0' -f2
    :  ASCII text
    
  2. The file command makes educated guesses about what type of file a file is.  Guesses, naturally, are sometime wrong.  For example, file will 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 from file is ASCII text, you want to see whether it says that the file is a text file.  If you look at the man page for file, you will see that it more-or-less promises to include the word text in its output if the file is a text file, but this might be in a context like shell commands text.  So, it may be better to check whether the output from file contains the word text:

    isFile=$(file -0 "$attachment" | cut -d $'\0' -f2)
    case "$isFile" in
       (*text*)
          echo "$attachment is a text file"
          ;;
       (*)
          echo "$attachment is not a text file, please use a different file"
          ;;
    esac
    
share|improve this answer

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.

share|improve this answer

Another option is to not use cut and to match a regex against the full output of file:

#...
isFile=$(file $attachment)
if [[ "$isFile" =~ ASCII ]]
#...
share|improve this answer
case $(file -b --mime-type - < "$attachment") in
  (text/*)
     printf '%s\n' "$attachment is probably text according to file"
     case $(file -b --mime-encoding - < "$attachment") in
       (us-ascii) echo "and probably in ASCII encoding"
     esac
esac
share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.