implement disown builtin

Closes #2810.

The syntax mirrors that of zsh.
This commit is contained in:
David Adam 2017-03-23 11:50:57 +11:00
parent f52708a20f
commit 4fde67fa50
6 changed files with 122 additions and 0 deletions

View file

@ -1,5 +1,11 @@
# fish 2.6.0 (released ???) # fish 2.6.0 (released ???)
## Notable fixes and improvements
- Jobs running in the background can now be removed from the list of jobs with the new `disown` builtin, which behaves like the same command in other shells (#2810).
## Other significant changes
- The `export` and `setenv` commands now supports colon-separated `PATH`, `CDPATH` and `MANPATH`. - The `export` and `setenv` commands now supports colon-separated `PATH`, `CDPATH` and `MANPATH`.
- The `read` command now has a default limit of 10 MiB. If a line is longer than that it will fail with $status set to 122 and the var will be empty. You can set a different limit by setting the FISH_READ_BYTE_LIMIT variable. - The `read` command now has a default limit of 10 MiB. If a line is longer than that it will fail with $status set to 122 and the var will be empty. You can set a different limit by setting the FISH_READ_BYTE_LIMIT variable.
- `read` now supports the `--silent` flag to hide the characters typed (#838). - `read` now supports the `--silent` flag to hide the characters typed (#838).

26
doc_src/disown.txt Normal file
View file

@ -0,0 +1,26 @@
\section disown disown - remove a process from the list of jobs
\subsection disown-synopsis Synopsis
\fish{synopsis}
disown [ PID ... ]
\endfish
\subsection disown-description Description
`disown` removes the specified <a href="index.html#syntax-job-control">job</a> from the list of jobs. The job itself continues to exist, but fish does not keep track of it any longer.
Jobs in the list of jobs are sent a hang-up signal when fish terminates, which usually causes the job to terminate; `disown` allows these processes to continue regardless.
If no process is specified, the most recently-used job is removed (like `bg` and `fg`). If one or more `PID`s are specified, jobs with the specified process IDs are removed from the job list. Invalid jobs are ignored and a warning is printed.
If a job is stopped, it is sent a signal to continue running, and a warning is printed. It is not possible to use the `bg` builtin to continue a job once it has been disowned.
The PID of the desired process is usually found by using <a href="index.html#expand-process">process expansion</a>, which can specify jobs or search by process name.
`disown` returns 0 if all specified jobs were disowned successfully, and 1 if any problems were encountered.
\subsection disown-example Example
`firefox &; disown` will start the Firefox web browser in the background and remove it from the job list, meaning it will not be closed when the fish process is closed.
`disown (jobs -p)` removes all jobs from the job list without terminating them.

View file

@ -3058,6 +3058,86 @@ static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
return res; return res;
} }
/// Helper for builtin_disown
static int disown_job(parser_t &parser, io_streams_t &streams, job_t *j) {
if (j == 0) {
streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"), L"bg");
builtin_print_help(parser, streams, L"disown", streams.err);
return STATUS_BUILTIN_ERROR;
}
// Stopped disowned jobs must be manually signalled; explain how to do so
if (job_is_stopped(j)) {
killpg(j->pgid, SIGCONT);
streams.err.append_format(
_(L"%ls: job %d ('%ls') was stopped and has been signalled to continue.\n"),
L"disown", j->job_id, j->command_wcstr());
}
if (parser.job_remove(j)) {
return STATUS_BUILTIN_OK;
} else {
return STATUS_BUILTIN_ERROR;
}
}
/// Builtin for removing jobs from the job list
static int builtin_disown(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
int res = STATUS_BUILTIN_OK;
if (argv[1] == 0) {
job_t *j;
// Select last constructed job (ie first job in the job queue) that is possible to disown.
// Stopped jobs can be disowned (they will be continued).
// Foreground jobs can be disowned.
// Even jobs that aren't under job control can be disowned!
job_iterator_t jobs;
while ((j = jobs.next())) {
if (j->get_flag(JOB_CONSTRUCTED) && (!job_is_completed(j))) {
break;
}
}
if (j) {
res = disown_job(parser, streams, j);
} else {
streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]);
res = STATUS_BUILTIN_ERROR;
}
} else {
std::set<job_t *> jobs;
// If one argument is not a valid pid (i.e. integer >= 0), fail without disowning anything,
// but still print errors for all of them.
// Non-existent jobs aren't an error, but information about them is useful.
// Multiple PIDs may refer to the same job; include the job only once by using a set.
for (int i = 1; argv[i]; i++) {
int pid = fish_wcstoi(argv[i]);
if (errno || pid < 0) {
streams.err.append_format(_(L"%ls: '%ls' is not a valid job specifier\n"), argv[0],
argv[i]);
res = STATUS_BUILTIN_ERROR;
} else {
if (job_t *j = parser.job_get_from_pid(pid)) {
jobs.insert(j);
} else {
streams.err.append_format(_(L"%ls: Could not find job '%d'\n"), argv[0], pid);
}
}
}
if (res == STATUS_BUILTIN_ERROR) {
return res;
}
// Disown all target jobs
for (auto j : jobs) {
res |= disown_job(parser, streams, j);
}
}
return res;
}
/// This function handles both the 'continue' and the 'break' builtins that are used for loop /// This function handles both the 'continue' and the 'break' builtins that are used for loop
/// control. /// control.
static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) { static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
@ -3542,6 +3622,7 @@ static const builtin_data_t builtin_datas[] = {
{L"continue", &builtin_break_continue, {L"continue", &builtin_break_continue,
N_(L"Skip the rest of the current lap of the innermost loop")}, N_(L"Skip the rest of the current lap of the innermost loop")},
{L"count", &builtin_count, N_(L"Count the number of arguments")}, {L"count", &builtin_count, N_(L"Count the number of arguments")},
{L"disown", &builtin_disown, N_(L"Remove job from job list")},
{L"echo", &builtin_echo, N_(L"Print arguments")}, {L"echo", &builtin_echo, N_(L"Print arguments")},
{L"else", &builtin_generic, N_(L"Evaluate block if condition is false")}, {L"else", &builtin_generic, N_(L"Evaluate block if condition is false")},
{L"emit", &builtin_emit, N_(L"Emit an event")}, {L"emit", &builtin_emit, N_(L"Emit an event")},

View file

@ -1,3 +1,4 @@
bg: '-23' is not a valid job specifier bg: '-23' is not a valid job specifier
fg: No suitable job: 3 fg: No suitable job: 3
bg: Could not find job '3' bg: Could not find job '3'
disown: 'foo' is not a valid job specifier

View file

@ -4,4 +4,9 @@ jobs -c
bg -23 1 bg -23 1
fg 3 fg 3
bg 3 bg 3
sleep 1 &
disown
jobs -c
disown foo
disown (jobs -p)
or exit 0 or exit 0

View file

@ -1,3 +1,6 @@
Command Command
sleep sleep
sleep sleep
Command
sleep
sleep