0

So I have a directory where I wish to open the only file in that directory that is human readable. I use file * to print the type of each file, and one file shows as ASCII-text. How can I redirect that specific file to cat or less to display its content?

EDIT:

Y'all are awesome. I'm trying each.

  • 1
    @Debian_yadav: no, he's got just one TEXT file among a bunch of binaries. – NickD Mar 22 '18 at 17:45
2

You can use awk to search for files containing ASCII text:

less $(file * | awk -F: '$2 ~ "ASCII text" {print $1}')

This actually works also for directories containing several text files.

  • 1
    Works as long as no filename has : in it. – Kusalananda Mar 22 '18 at 17:53
  • @Kusalananda Truely true, there is always file -0 :-) – nohillside Mar 22 '18 at 17:59
  • Is there? Possibly on Linux... – Kusalananda Mar 22 '18 at 18:01
  • @Kusalananda At least on macOS (and, according to the man page, BSD and Debian) – nohillside Mar 22 '18 at 18:04
  • 1
    I think you'd have to double-quote the command substitution to deal with files with $IFS in their names. – Jeff Schaller Mar 22 '18 at 18:07
1

The following bash function will look through the files in the current directory; if exactly one of them reports back as being "ASCII text", then it will cat that file.

filecat() {
  local files=(./*)
  local count=0
  local filename=
  local f=
  for f in "${files[@]}"
  do
    if file "$f" 2>/dev/null | grep -q ": ASCII text$"
    then
      count=$((count + 1))
      filename="$f"
    fi
  done
  if [ "$count" -eq 1 ]
  then
    cat "$filename"
  fi
}
1

With zsh, you could define a function like:

 istext() [[ $(file -b --mime-type -- "${1-$REPLY}") = text/* ]]

Which you could then use in glob qualifiers like:

 less -- *(.L+0+istext)

To view the non-empty (L+0 for length greater than 0) regular files (.) in the current directory that are text according to file.

1

Here's a safe way to open what file thinks are text files in the current directory in vim. Of course, you can change vim to echo to just print the names.

#!/bin/bash

for f in *; do
   out=$(file "$f")
   [[ "${out##*: }" =~ ASCII ]] && text_files+=("$f")
done

vim "${text_files[@]}"

exit

EDIT: use two # signs in the parameter expansion to handle filenames with a : in them.

0

To only less a file if it's an ASCII text:

less_if_text() {
   # $1 is the file name
   if file $1 | grep -q 'ASCII .* text' ; then
     less $1
   else
     echo "Not a text file: $1"
   fi
}

To list text files in a naive but easy to understand way:

ls_texts() {
  ls -a | while read filename; do 
    (file $filename | grep -q 'ASCII .* text') && echo $filename
  done
}

The above is not quite fast, though. A faster way would use file -f to avoid multiple file and grep invocations:

ls_texts() {
  ls | file -f - | egrep ':\s+ASCII .* text' | while read fname
    do cut -d ':' -f 1 
  done
}
  • If they already have the filename in hand ($1, the parameter to your function), then they could jus tpass it to cat or less themselves. Perhaps a more generic answer would run file on every file in the current (or given?) directory and go from there? – Jeff Schaller Mar 22 '18 at 17:51
  • @JeffSchaller: Updated the answer to include the listing of files. – 9000 Mar 22 '18 at 18:26
  • parsing ls is tricky... – Jeff Schaller Mar 22 '18 at 18:27
  • @JeffSchaller: Indeed, especially if you have file names with embedded spaces and other special characters; add --quote-names to alleviate it a bit. – 9000 Mar 22 '18 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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