Home > Net >  I get an error when I am try using -ls with different directory. And I don't know why
I get an error when I am try using -ls with different directory. And I don't know why

Time:08-05

I am using -ls to check if my files exist. The problem is when I run this:

infile=/Volumes/malrosie/data/satellite/chlor_a/cmems/mapped/4km/daily/*.nc
ls -l $infile | tail -n 1

I get what I want:

-rw-r--r--  1 malrosie  staff   91748221 Nov 12  2021 /Volumes/malrosie/data/satellite/chlor_a/cmems/mapped/4km/daily/20210630_d-ACRI-L4-CHL-MULTI_4KM-GLO-REP.nc

But when I run the same command on a different directory:

infile1=/Volumes/malrosie/data/satellite/chlor_a/cmems/mapped/4km/daily_crop/*.nc
ls -l $infile1 | tail -n 1

I get an error:

bash: /bin/ls: Argument list too long

Why is this? How do I solve it?

CodePudding user response:

Your list is longer than can fit on an command line.


Making The ls Run Anyhow

files=( /Volumes/malrosie/data/satellite/chlor_a/cmems/mapped/4km/daily/*.nc )
printf '%s\0' "${files[@]}" | xargs -0 ls -l | tail -n 1

Because printf is built into the shell, it isn't subject to limits on the maximum number of characters that can be on a command line. We're then feeding the output of printf into xargs, which is responsible for starting ls -l as many times as necessary to get through the filenames on its stdin.

Because of the tail -n 1, all but the last copy of ls will have its results thrown away, making the existence of the other copies pointless.


If ls isn't actually important to you

You don't need tail to check if your files exist, and using it unnecessarily is slow: If even one file exists, your glob expression succeeded. Thus, a fast and terse approach is:

files=( /Volumes/malrosie/data/satellite/chlor_a/cmems/mapped/4km/daily/*.nc )
if (( ${#files[@]} > 1 )); then
  echo "Great: ${#files[@]} files exist" >&2
elif (( ${#files[@]} == 1 )); then
  if [[ -L ${files[0]} || -e ${files[0]} ]]; then
    echo "One file exists, ${files[0]}" >&2
  else
    echo "No files exist" >&2
  fi
else
  echo "No files exist, and nullglob is enabled" >&2
fi

Here, we're assigning the results of the glob to an array named files, after which we can refer to ${#files[@]} to get the number of files named in the array (which may be 1 even if there are no entries due to default globbing behavior).

Thus:

  • If the number of array entries is more than one, there definitely do exist matching files.
  • If the number of array entries is exactly one, it may contain a glob expression (.../*.nc) instead of a file, so we need to check whether the first entry (the one at index 0) either exists, or is a symlink (in which case the existence check can say false if the link itself exists but points to a location where nothing exists).
  • If the number of array entries is zero, it definitely contains no files.
  • Related