-1

I want to count the number of files matching a pattern at a depth of 1, and compare it with a number, e.g. to see if I have 3 or more such files. However, I want to do this without using the if/then syntax.

i.e. something like this:

ls -1q -d *patternX* | wc -l | [ -ge 3]

I'd appreciate very much if someone could let me know if there's a way to escape using the if/then syntax. Thanks!

abra
  • 65
  • 4
    "I want to do this without using the if/then syntax" -- So, is there some reason to not use a construct made exactly for conditional execution if conditional execution is what you want/need to do? If you've just arbitrarily decided that the obvious solution is not acceptable, than how can we know if some other solution would also not be acceptable? – ilkkachu Apr 23 '21 at 12:54
  • 1
    As commented already, if you have any constraints (i.e., "not using ''if'"), please explain why such a constraint exists. We cannot offer much help if you provide arbitrary constraints that limit how we can help. – C. M. Apr 23 '21 at 13:30
  • @ilkkachu Thanks for commenting. I'm familiar with the if/then syntax and have used it in the past. I was curious whether the same aim could be achieved this way (piping "directly" into an inequality comparison), and therefore asked the question in the title out of pure curiosity/academic interest. – abra Apr 24 '21 at 13:01
  • @C.M. Thanks for commenting. I asked the question in the title not out of necessity, but out of pure curiosity/academic interest. I keep learning new things everyday; I wondered whether it could be done this way and thought if anyone knew, it would be someone on this site, and therefore I asked. – abra Apr 24 '21 at 13:05
  • @abra: I suggest you edit your question to indicate that, so it's up front instead of someone having to read through the comments down here. I will note, however, that your "compare it ... and if ..." statements make it very difficult to do without some kind of logic branching (i.e., using if), as your requirement itself has an "if". You can save the wc output to a shell variable, but without knowing more about your goal, that may be useless. (I.E., let's say you can [ $result -gt 3 ] ... but what do you want to do with the comparison? it will just be a true/false boolean value.) – C. M. Apr 25 '21 at 05:36
  • (and to add given comments posted to some answers): Just having stating it in the title is not enough. The title often only gets the attention of those who want to help, or those seeking a solution to a similar/identical problem. Then they read the body of the question to find out the full details--even ones already stated in the title. – C. M. Apr 25 '21 at 05:44

3 Answers3

5

You seem to want to count the number of names matching *patternX* and then test whether that is greater than or equal to three.

This is best done like so:

shopt -s nullglob

set -- patternX if [ "$#" -ge 3 ]; then echo 'More than 2 names match the pattern' fi

This is setting the positional parameters to the names matching the pattern. The number of matching names would be kept in $#. The nullglob option is set so that if there are no names matching, the pattern is removed completely rather than kept unexpanded.

You could also use a named array to store the matching names:

shopt -s nullglob

names=(patternX) if [ "${#names[@]}" -ge 3 ]; then echo 'More than 2 names match the pattern' fi

See also Why *not* parse `ls` (and what to do instead)?


Without the words if and then:

shopt -s nullglob

set -- patternX [ "$#" -ge 3 ] && echo 'More than 2 names match the pattern'

A similar approach using awk:

shopt -s nullglob

awk 'BEGIN { if (ARGC > 3) print "more than 2" }' patternX

Note that ARGC in awk also accounts for the command name (what would be $0 in the shell).

Kusalananda
  • 333,661
  • Hi! Thank you, but my question specifically asks whether it is possible to do this without using the "if;then" syntax, so this isn't what I'm looking for. – abra Apr 23 '21 at 12:07
  • @abra See updated answer – Kusalananda Apr 23 '21 at 12:09
  • Thank you! Your second suggestion doesn't have "if" and "then", but it's mostly a technicality as the underlying method is similar. As I mentioned in the title, I am looking for a syntax (if possible) where I can pipe the output of a command (like wc) directly into an inequality operation. – abra Apr 23 '21 at 12:16
  • 1
    @abra The issue is that the operation that you seem to want to do, i.e. count names matching a pattern, should not generally be done with ls and wc. – Kusalananda Apr 23 '21 at 12:22
  • Tests do not read stdin, so you need something inside the test that does read the pipe: echo 7 | [ $( cat ) -ge 6 ] && echo Bigger || echo Not. – Paul_Pedant Apr 23 '21 at 12:25
  • 2
    @abra so, like [ "$(... | wc)" -ge 3 ]? – muru Apr 23 '21 at 12:28
  • @Paul_Pedant thanks, much closer to what I want, but I would want the command to print 0 or 1 at the end; if I cut off the " &&...." part of your suggestion, it doesn't return any output – abra Apr 23 '21 at 12:35
  • Test returns a true (0) false (1) status. echo 7 | [ $( cat ) -ge 6 ]; echo $?. – Paul_Pedant Apr 23 '21 at 12:39
  • 2
    @abra please edit your question and add all of these requirements. What you are asking for is not a standard feature and most of us are happy using either if/else or condition && foo || bar constructs so if you cannot use that we need to know. – terdon Apr 23 '21 at 15:16
  • @terdon the title of the question mentions all these requirements. The question is less out of necessity and more out of curiosity because it's the syntax that came first/intuitively to my mind, but I couldn't find any solutions along those lines. So I thought I'd consult the community since I'm a relative beginner and see if it's possible. I'm not necessarily asking if it's the best/most common solution. – abra Apr 24 '21 at 07:46
  • 1
    @abra So you don't need to count the number of files in a directory in a correct way, or even interested in how that should be done in a portable and idiomatic way? You're just interested in how to do something that nobody would want to do, just to see whether it's possible? – Kusalananda Apr 24 '21 at 07:49
  • @Kusalananda It's not that I'm uninterested in how to do it the correct way. I'm familiar with the if/then syntax and have used it in the past. I was curious whether the same aim could be achieved this way, and therefore asked. I assumed this was a place where questions of pure curiosity/academic interest were welcome. – abra Apr 24 '21 at 12:56
2

With zsh, you'd typically use an anonymous function such as:

()(( $# >= 3 )) *patternX*(N)

That calls an anonymous function whose body is (( $# >= 3 )) (which returns true if the number of arguments is 3 or more), with the expansion of the *patternX*(N) glob as arguments.

(N) is to enable the nullglob option for that glob so that the glob expands to nothing instead of reporting an error if there's no matching file.

A more literal answer to what you're asking could be:

ls -1q -d -- *patternX* | wc -l | xargs -IN test N -ge 3

(in any shell, but beware behaviour varies between shell when the pattern doesn't match any file).

Or:

[ "$(( $(ls -1q -d -- *patternX* | wc -l) ))" -ge 3 ]

(in any POSIX or Korn-like shell)

0

I find it hard to believe that this is a real world problem, but here's one solution:

ls -1q -d *patternX* | wc -l | egrep '^[3-9][0-9]*|[0-9][0-9]+' && echo JA
Ljm Dullaart
  • 4,643