6

I would like to write a while loop in Bash that runs over all instances of files that are of the form of

{number}.tst

for example, 1.tst, 2.tst, ... 50.tst.

I do not want it to run over the file tst.tst.

How would I go about writing this? I assume I will need a boolean phrase and [0-9]* somewhere in there, but I am not entirely sure on the syntax.

  • Are your numbers all positive integers? :) – Sebastian Stark Jul 2 '18 at 21:08
  • Correct, all positive. – J. Doe Jul 2 '18 at 21:09
8

If you only need to exclude alphabetic names like your example tst.tst you could use a simple shell glob

for f in [0-9]*.tst; do echo "$f"; done

With bash extended globs (which should be enabled by default in Ubuntu)

given

$ ls *.tst
1.tst  2.tst  3.tst  4.tst  50.tst  5.tst  bar.tst  foo.tst

then +([0-9]) means one or more decimal digits:

for f in +([0-9]).tst; do echo "$f"; done
1.tst
2.tst
3.tst
4.tst
50.tst
5.tst

You can check whether extended globbing is enabled using shopt extglob and set it if necessary using shopt -s extglob (and unset using set -u extglob).

  • This works for an echo loop, but the variable itself becomes [0-9]*.tst. How could I make it so just the numerical value is there? Say I would like to use the number associated with the file name later on in the script with a different name. For example, if the file name is 9.tst, but later on in the loop, I would like to apply changes to a file named 9-tst.tst. The numerical value at the beginning is the same, but the file cannot be opened because the script reads as 9.tst-tst.tst when trying to open. – J. Doe Jul 2 '18 at 21:05
  • @J.Doe you can remove the extension using parameter expansion e.g. if f contains 9-tst.tst then "${f%.tst}" will remove the shortest trailing string matching .tst to leave just 9-tst – steeldriver Jul 2 '18 at 21:11
  • Now I seem to be running into another problem where the variable reads quite literally as [0-9]*-tst.tst when written into my full script. – J. Doe Jul 2 '18 at 21:20
  • @J.Doe that will happen if there are no matching files in the directory, and you did not set the nullglob option (shopt -s nullglob) – steeldriver Jul 2 '18 at 21:23
  • Oh duh, it was just a path issue. Thank you again. – J. Doe Jul 2 '18 at 21:34
6

From this Stack Overflow answer: List files that only have number in names:

find . -regex '.*/[0-9]+\.tst'

OR

Using find also has advantages when you want to do something with the files, e.g. using the built-in -exec, -print0 and pipe to xargs -0 or even (using Bash):

while IFS='' read -r -d '' file
do
  # ...
done < <(find . -regex '.*/[0-9]+\.tst' -print0)

Note the other answers here my include files that aren't numbers if the filename starts with a digit. The answer posted here does not though. For example:

$ ls *.tst
12tst.tst  1.tst  2.tst

$ find . -maxdepth 1 -regex '.*/[0-9]+\.tst'
./1.tst
./2.tst

NOTE: Use -maxdepth 1 argument to only list numbered files in the current directory and not in sub-directories.

  • 3
    Especially with "-exec", or the "-print0 | xargs -0" options, using "find" is clearly superior to shell globbing for non-trivial tasks: it handles all of the issues around weird characters in file names and argument length limits with grace and aplomb. – minnmass Jul 2 '18 at 22:07
  • @minnmass Soooo true! I had a nightmare a few months ago around weird filenames that caused all kinds of headaches. In this case of where only simple filenames are used it's not a problem but perhaps it's good to future proof the code. – WinEunuuchs2Unix Jul 2 '18 at 22:30
  • 1
    find is recursive, the ls variants are not. You may want to add the depth parameter to the find command line. – allo Jul 3 '18 at 8:45
  • @allo Good point. But it also may be a benefit to list numbered files in sub-directories too. I've added -maxdepth to the answer with a note. – WinEunuuchs2Unix Jul 3 '18 at 10:14
4

In this case, there are no filenames of the form {number}{non-number}.tst, so one possible solution is to include all the filenames that start with a number:

for filename in [0-9]*.tst; do
    echo "$filename"  # Example command
done
  • 1
    Oh... so you mean 123foo.tst would be included? – WinEunuuchs2Unix Jul 2 '18 at 20:08
  • 1
    @WinEunuuchs2Unix Yes, any filename that starts with a number and ends with .tst would be included, regardless of what's in the middle. – wjandrea Jul 2 '18 at 20:17
  • I assume it's the same with steeldriver's recent edits then. I've edited my answer to show files like 123foo.tst would be excluded. – WinEunuuchs2Unix Jul 2 '18 at 20:25
  • @Win Yes, it's the same glob as in steeldriver's edit. – wjandrea Jul 2 '18 at 20:30
0

If you want to process the files in numerical order, there's the seq command (read man seq).

for i in $( seq 1 50 ) ; do
    echo "Process ${i}.txt"
done
  • I don't get it. File 1.txt, 10.txt and 20.txt might exist but doesn't your script simply list all potential filenames from 1.txt to 50.txt? – WinEunuuchs2Unix Jul 3 '18 at 2:05
  • @WinEunuuchs2Unix Yup, that's exactly it. The * glob in shell expands to existing filenames. Here's it's not a requirement, and if you know, files are there, there's no need to check for their existence. This is perfectly acceptable, although not efficient when the number of files gets large. Shell loops,especially that employ seq aren't efficient. Whole programs written in C are more efficient, like find . – Sergiy Kolodyazhnyy Jul 3 '18 at 3:24
  • @SergiyKolodyazhnyy OK, I'm not sure I totally understand but what if there is a file named 51.txt wouldn't that be excluded? – WinEunuuchs2Unix Jul 3 '18 at 3:26
  • @WinEunuuchs2Unix Yes, that would be excluded. And again, this way is useful when you know that there are exactly files 1.txt to 50.txt. If you have files 1 to 51, then you'd do seq 1 51. In other words, here you have more of your own control, with something like +([0-9]).txt you want shell to figure it out for you. Basically, depends on how much you want shell to handle :) – Sergiy Kolodyazhnyy Jul 3 '18 at 3:32

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.