2

I'm running on a server where qstat gives me the IDs of running jobs (details not important). E.g., say I have three jobs running, 123, 124, 125, then

> qstat
123
124
125

Each job has a log file in logs/. I want to tail -F the log files of all jobs.

I attempted

tail -F $(qstat | awk -v P=logs '{ print P"/*"$1 }' | paste -sd " ")

but I get

tail: cannot open logs/*123 for reading: No such file or directory

for each job.


I'm running zsh, the same thing works in bash!

Apparently, there is no wild card expansion after $(...), could that be? How do I fix this for zsh?

  • I think this has been asked before... can't find it now though... Anyway, you need setopt globsubst to enable filename generation. – don_crissti Sep 6 '18 at 13:38
  • 3
    Possible duplicate of Glob character within variable expands in bash but not zsh. See also How to use parameter substitution in glob pattern (zsh) for another approach... – don_crissti Sep 6 '18 at 13:48
  • @don_crissti, those are about parameter expansion while here it's about command substitution. That's a bit different in that zsh does neither split nor glob upon parameter expansion though it does split upon command substitution, so it's possibly misleading to claim that the issues are duplicate as it could make people think zsh doesn't split upon command substitution. – Stéphane Chazelas Sep 7 '18 at 7:29
  • @StéphaneChazelas - fair enough... I'm retracting my vote though this is in the review queue now and it might get closed eventually. – don_crissti Sep 7 '18 at 16:30
4

Yes, doing filename expansion upon command substitution and other expansions is generally not wanted¹ and not done by default in zsh except in sh/ksh emulation (globsubst option).

While you could use ${~$(...)} to request the use of globsusbt for that particular command substitution (and by the way, you don't need the paste part, both space and newline are in the default value of $IFS), a much better way to do it with zsh would be:

ids=($(qstat))
tail -F logs/*${^ids}

Note that if any of those globs fail to match any file, the command will be aborted.

tail -F logs/*${^ids}(N)

(where (N) activates nullglob for those globs) would avoid that but would run tail -F without argument if there was no file at all (see Why is nullglob not default?).

You could also make it:

logs=(logs/*${^$(qstat)}(N))
(($#logs)) && tail -F $logs

¹ The fact that bash and other Bourne-like shells do it could be seen as a bug. That's one reason why you need to quote all your variables there, or why you need set -o noglob before using an unquoted $(...) when you only want the splitting part of that split+glob. All more modern shells that don't carry the Bourne shell baggage like rc, es or fish don't do it either.

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.