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. Commented 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. Commented Mar 22, 2018 at 17:53
  • @Kusalananda Truely true, there is always file -0 :-) Commented Mar 22, 2018 at 17:59
  • Is there? Possibly on Linux... Commented Mar 22, 2018 at 18:01
  • @Kusalananda At least on macOS (and, according to the man page, BSD and Debian) Commented 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. Commented 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? Commented Mar 22, 2018 at 17:51
  • @JeffSchaller: Updated the answer to include the listing of files. Commented Mar 22, 2018 at 18:26
  • parsing ls is tricky... Commented 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. Commented Mar 22, 2018 at 19:15

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.