The equivalent of cmd "for" in bash?

I only know little about bash (or any Linux shell – but I just use bash). I’m used to using for %c in (*.*) do {something} in cmd in Windows, and I really feel inconvenient without it. I tried wine cmd, but it wouldn’t interpret Linux commands (which I need for the {something}) at all, which is not I want. Any help?

In Windows, for %c in (*.*) do {something} runs {something} once per file (ignoring directories). {something} can refer to %c for the current iteration’s file name; other modifiers allow the file’s path to be extracted, or the file’s name without its path, the file’s extension, attributes, modification date/time, size… for /d %c in (*.*) ... will process directories instead of files. In both cases *.* is equivalent to * in Unix-style systems, it matches all non-hidden files or directories, even those with no . in their name.

P.S. I know that the cmd “for” has many usages which may not be covered in one command in bash, but I just want to know the equivalent of the command mentioned above.

Asked By: jack980517

for file in *; do
  echo "$file"

This usually does not match “hidden” files (names beginning with a dot) though.

For matching the hidden files, too, you need this before:

shopt -s dotglob

If you want to skip directories (and probably anything “strange”) then you have to detect them in the loop; pattern matching (the *) doesn’t care about the type of object:

for file in *; do
  test -f "$file" || continue
  echo "$file"

Symbolic links are a special case. They are considered if they link to a file but the file may be in another directory. To ignore symlinks:

for file in *; do
  test -f "$file" || continue
  test -L "$file" && continue
  echo "$file"
Answered By: Hauke Laging

if you interesting in for loop learning

syntax –

for i in $(command with iterable elements); do command $i; done

like –

for i in $(echo 1 2 3 4 5); do echo $i; done

if you interesting in search command –

find . -name "*"

with depth level –

find . -maxdepth 1 -name "*"
find . -maxdepth 2 -name "*"

maybe it more useful for you

Answered By: dmgl

The pattern matching behavior you describe is – according to this technet blog – a grandfathered bug:

…you’ll also discover that *.* is the same as * by itself.

In addition … if you typed DIR .TXT, the command prompt acted as if you had typed DIR *.TXT… This behavior was probably … not intentional, but it was an accident that some people came to rely upon. When we fixed the bug in Windows 95, more than one person complained that their DIR .TXT command wasn’t working.

The FCB matching algorithm was abandoned during the transition to Win32 since it didn’t work with long file names. Long file names can contain multiple dots, and of course files can be longer than eleven characters, and there can be more than eight characters before the dot. But some quirks of the FCB matching algorithm persist into Win32 because they have become idiom.

For example, if your pattern ends in .*, the .* is ignored. Without this rule, the pattern *.* would match only files that contained a dot, which would break probably 90% of all the batch files on the planet, as well as everybody’s muscle memory, since everybody running Windows NT 3.1 grew up in a world where *.* meant all files.

As another example, a pattern that ends in a dot doesn’t actually match files which end in a dot; it matches files with no extension. And a question mark can match zero characters if it comes immediately before a dot.

This may not differ too terribly from the origin of the Unix precedent for dot files, as Rob Pike notes in his blog:

Long ago, as the design of the Unix file system was being worked out, the entries . and .. appeared, to make navigation easier. I’m not sure but I believe .. went in during the Version 2 rewrite, when the file system became hierarchical (it had a very different structure early on). When one typed ls, however, these files appeared, so either Ken or Dennis added a simple test to the program. It was in assembler then, but the code in question was equivalent to something like this:

if (name[0] == '.') continue;

This statement was a little shorter than what it should have been, which is

if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;

…but hey, it was easy…

I’m pretty sure the concept of a hidden file was an unintended consequence. It was certainly a mistake.

(For those who object that dot files serve a purpose, I don’t dispute that but counter that it’s the files that serve the purpose, not the convention for their names. They could just as easily be in $HOME/cfg or $HOME/lib, which is what we did in Plan 9, which had no dot files. Lessons can be learned.)

Since then, though, the . dot files came to be associated with the standard shell . utility, and from there just with user-specific plain-text configuration files.

You won’t typically find dot files anywhere but a user’s ~ because that is really the only place they would make sense – a user shouldn’t be keeping any of their files anywhere else in the filesystem, anyway. When working on files in one’s home directory it would be unfortunate if a user were to accidentally remove some or all of their configuration files – but it would be even more unfortunate if – when in ~ or any other directory – a loop were to unintentionally affect . – the current directory – or (worse still) .. – the parent directory. POSIX has this to say about .:

If a filename begins with a period ., the period shall be explicitly matched by using a period as the first character of the pattern or immediately following a /slash character. The leading period shall not be matched by:

  1. The *asterisk or ?question-mark special characters

  2. A [bracket] expression containing a non-matching list, such as [!a], a range expression, such as "[%-0]", or a character class expression, such as [[:punct:]]

    • It is unspecified whether an explicit period in a bracket expression matching list, such as [.abc], can match a leading period in a filename.

Still, though, it is possible to loop over all files in the current directory – dot files or not – in a single loop simply enough. For example:

set -- .* *
while until [ -f "$1" ] ||
            [ -z "$1" ] &&
            break "$((!$#+1))"
      do    shift;   done
do    printf %s\n  "$1"

The above prints all regular files in the current directory in dash, bash, ksh, zsh, mksh, posh, and yash. It is even more akin to the Windows FOR loop than is the typical shell for in that this doesn’t set any permanent shell variables – just like a cmd FOR it processes its arguments and when it is through with them their names no longer hold any value. In a typical shell for the last value in $var in for var in ... remains.

It doesn’t have to be like that:

set -- .* * ''
while [ -n "$1" ]  || ! shift
do    [ -f "$1" ]  &&
      set -- "$@" "$1"
printf %s\n "$@"

…accomplishes the same goal just as portably – printing all regular files in the current directory – but it loops over the arguments once first and prunes those which are not regular files. The advantage, though, is that afterward the shell’s arg "$@" array still holds the whole list, and from that point on you can do:

for f do : something w/ "$f"; done

…as many times as you please on the same array. But you also get the count in "$#" and all args concatenated into a single string in "$*" and each addressable singly in sorted order in "$1""${10}" and so on.

Those are typically not the kind of things you can do w/ Win32 cmd.

Answered By: mikeserv
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.