4
$ sleep 1234&
$ sleep 12345678&
$ %sleep\ 1234
bash: fg: sleep 1234: ambiguous job spec
$ jobs
[1]-  Running                 sleep 1234 &
[2]+  Running                 sleep 12345678 &

Sure, I can just use %1 etc. to foreground the first job.

But I am just curious, am I out of luck still trying to figure out a way to match only the first name, but not also the second, using matching strings?

(By the way, %sle<TAB> doesn't have tab expansion yet.)

2 Answers 2

4

That syntax comes from csh along with job control in 4BSD in 1980, and has been copied by ksh circa 1983¹, the Bourne shell in 1989 (SVR4), Almquist shell in 1989, bash in 1989, zsh in 1990, and was specified in XPG4 in 1992 (which later became the Single UNIX Specification later merged with POSIX) and so supported by all modern sh implementations.

That %something as a shorthand for fg %something is not in ksh/POSIX, only in (t)csh, bash, zsh.

The job specification syntax is described in the csh manual starting with 4BSD's or in the XPG/POSIX/SUS specification, and the manual of each shell that supports it. For instance in bash's there.

In all, it's the same:

  • %something where something is not a number and doesn't start with ? looks for jobs whose text starts with something
  • %?something (where in POSIX-like shells including bash, but not zsh², the ? must be quoted/escaped to prevent globbing) looks for jobs whose text contains something.

So as @AndyDalton said, in bash and most other shells, there's no way to distinguish sleep 1234 from sleep 123456.

%'sleep 1234' or %'?sleep 1234' would match both. Depending on the shell, if there's more than one match, either an error is returned ((t)csh, bash, ash, pdksh, bosh, yash), or the latest matching job is selected (ksh, zsh). POSIX is silent on the matter.

In ksh93 however, if you started sleep 1234 as sleep 1234 & instead of sleep 1234&, you'll be able to refer to it as %'sleep 1234 ' with the trailing space, and disambiguate it from the one started as sleep 12345 & or sleep 12345&.

In zsh, information about the job table is exposed via a set of special associative arrays: jobdirs, jobtexts and jobstates which map job number to the corresponding information. For example:

$ sleep 1234 & cd /tmp;  sleep 12345 & sleep 12 | sleep 1234 &
[1] 18016
[2] 18017
[3] 18018 18019
$ printf '%s => %s\n' ${(kv)jobdirs}
1 => /home/chazelas
2 => /tmp
3 => /tmp
$ printf '%s => %s\n' ${(kv)jobtexts}
1 => sleep 1234
2 => sleep 12345
3 => sleep 12 | sleep 1234
$ printf '%s => %s\n' ${(kv)jobstates}
1 => running::18016=running
2 => running:-:18017=running
3 => running:+:18018=done:18019=running

So you could derive the job number from the text by doing reverse subscripting on the $jobtexts associative array.

To put the job running sleep 1234 in foreground:

fg %${(k)^jobtexts[(R)sleep 1234]}

Where the R array subscript flag selects all elements for which the value if sleep 1234, the k parameter expansion flag causes the key instead of value to be expanded, ^ is so that if there are several matching jobs (x, y...), it becomes %x %y... instead of %x y....

If there are several matching jobs, they will be brought to foreground one after the other.

You could even hijack the dynamic named directory feature to have ~[%pattern] expand to the list of matching job numbers (and raise an exception if there's no match) by defining:

job_handler() if [[ $1$2 = n%* ]] reply=(%${(k)^jobtexts[(R)${2#%}]})
zsh_directory_name_functions+=( job_handler )

Then in your example:

fg ~[%*1234]

Would foreground all the jobs whose text ends in 1234 for instance.


¹ See this usenet article confirming it in early 1984

² In zsh, %? at the start of a word is special-cased so that particular ? is not treated as a glob operator. If you really wanted to expand a %?something glob (expand to filenames in current working directory that start with %, followed by any single character and something), you'd need to write it [%]?something for instance.

3

I'm afraid you're out of luck on that one.

The %sleep... is a prefix pattern for a command, and it has to resolve to a unique command

$ '%sleep 1'     # Fails - matches both
$ '%sleep 12'    # Fails - matches both
$ '%sleep 123'   # Fails - matches both
$ '%sleep 1234'  # Fails - matches both
$ '%sleep 12345' # Succeeds - unique match

From Bash/Job Control Basics:

A job may also be referred to using a prefix of the name used to start it, or using a substring that appears in its command line. For example, ‘%ce’ refers to a job whose command name begins with ‘ce’. Using ‘%?ce’, on the other hand, refers to any job containing the string ‘ce’ in its command line. If the prefix or substring matches more than one job, Bash reports an error.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.