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
  • 1
    @Debian_yadav: no, he's got just one TEXT file among a bunch of binaries.
    – NickD
    Mar 22, 2018 at 17:45

5 Answers 5

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.

6
  • 1
    Works as long as no filename has : in it.
    – Kusalananda
    Mar 22, 2018 at 17:53
  • @Kusalananda Truely true, there is always file -0 :-)
    – nohillside
    Mar 22, 2018 at 17:59
  • Is there? Possibly on Linux...
    – Kusalananda
    Mar 22, 2018 at 18:01
  • @Kusalananda At least on macOS (and, according to the man page, BSD and Debian)
    – nohillside
    Mar 22, 2018 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, 2018 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
}
4
  • 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, 2018 at 17:51
  • @JeffSchaller: Updated the answer to include the listing of files.
    – 9000
    Mar 22, 2018 at 18:26
  • parsing ls is tricky...
    – Jeff Schaller
    Mar 22, 2018 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, 2018 at 19:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .