[PATCH] glob: add shopt globmtimesort to sort globs by mtime
--- There is currently no good way to sort files by mtime in the shell. It's possible to do so with an ls that supports -t, but parsing ls is problematic. It's possible using GNU find's printf %T and nul separated lists with sort -z. Neither is a great option. This patch adds the ability to sort globs by mtime with shopt -s globmtimesort. I don't think it's worth adding atime and ctime, but I know someone will want it so it's coded in a way that should make that easy to implement once you figure out the shopt interface for it. I feel like it would may better to stat files while globbing, but that's a much larger change. This seemed like the smallest addition to get this working. I'm not familiar with the bash codebase so there's a good chance I got parts of this wrong. Is this the right place for the code? What should happen if stat fails? Did I code stuff in an unportable way? Looking forward to feedback. builtins/shopt.def | 2 ++ pathexp.c | 56 +- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/builtins/shopt.def b/builtins/shopt.def index 33d61d4c..48fd7dc0 100644 --- a/builtins/shopt.def +++ b/builtins/shopt.def @@ -89,6 +89,7 @@ extern int check_jobs_at_exit; extern int autocd; extern int glob_star; extern int glob_asciirange; +extern int glob_mtimesort; extern int glob_always_skip_dot_and_dotdot; extern int lastpipe_opt; extern int inherit_errexit; @@ -211,6 +212,7 @@ static struct { { "force_fignore", &force_fignore, (shopt_set_func_t *)NULL }, #endif { "globasciiranges", &glob_asciirange, (shopt_set_func_t *)NULL }, + { "globmtimesort", &glob_mtimesort, (shopt_set_func_t *)NULL }, { "globskipdots", &glob_always_skip_dot_and_dotdot, (shopt_set_func_t *)NULL }, { "globstar", &glob_star, (shopt_set_func_t *)NULL }, { "gnu_errfmt", &gnu_error_format, (shopt_set_func_t *)NULL }, diff --git a/pathexp.c b/pathexp.c index 379128e7..97f9a7f3 100644 --- a/pathexp.c +++ b/pathexp.c @@ -33,6 +33,9 @@ #include "pathexp.h" #include "flags.h" +#include "posixstat.h" +#include "stat-time.h" + #include "shmbutil.h" #include "bashintl.h" @@ -53,6 +56,9 @@ int extended_glob = EXTGLOB_DEFAULT; /* Control enabling special handling of `**' */ int glob_star = 0; +/* Control whether globs are sorted by mtime. */ +int glob_mtimesort = 0; + /* Return nonzero if STRING has any unquoted special globbing chars in it. This is supposed to be called when pathname expansion is performed, so it implements the rules in Posix 2.13.3, specifically that an unquoted @@ -402,6 +408,52 @@ quote_globbing_chars (string) return temp; } +struct glob_time { + char *name; + struct timespec time; +}; + +static int +glob_time_cmp(g1, g2) + struct glob_time *g1; + struct glob_time *g2; +{ + return -timespec_cmp(g1->time, g2->time); +} + +static void +glob_time_sort (array, get_stat_time) + char **array; + struct timespec (*get_stat_time)(struct stat const *); +{ + struct glob_time *gt_array; + int len, i; + struct stat buf; + + len = strvec_len (array); + + gt_array = (struct glob_time*)xmalloc (len * sizeof (struct glob_time)); + + for (i = 0; i < len; i++) +{ + gt_array[i].name = array[i]; + if (!stat (array[i], &buf)) + gt_array[i].time = get_stat_time (&buf); + else + { + gt_array[i].time.tv_sec = -1; + gt_array[i].time.tv_nsec = -1; + } +} + + qsort (gt_array, len, sizeof (struct glob_time), (QSFUNC *)glob_time_cmp); + + for (i = 0; i < len; i++) +array[i] = gt_array[i].name; + + free (gt_array); +} + /* Call the glob library to do globbing on PATHNAME. */ char ** shell_glob_filename (pathname, qflags) @@ -422,7 +474,9 @@ shell_glob_filename (pathname, qflags) { if (should_ignore_glob_matches ()) ignore_glob_matches (results); - if (results && results[0]) + if (results && results[0] && glob_mtimesort) + glob_time_sort (results, get_stat_mtime); + else if (results && results[0]) strvec_sort (results, 1); /* posix sort */ else { -- 2.36.1
Re: [PATCH] glob: add shopt globmtimesort to sort globs by mtime
On Mon Nov 14, 2022 at 1:00 PM MST, Chet Ramey wrote: > On 10/3/22 2:56 PM, Evan Gates wrote: > > --- > > > > There is currently no good way to sort files by mtime in the shell. > > It's possible to do so with an ls that supports -t, but parsing ls is > > problematic. > > Thanks for the patch. There are some good things here, usable stuff, but I > think I'll look at a more general approach for the next version. If you're taking care of it, that's awesome. If you have feedback and want me to take another shot, I'd be happy to try. It would definitely take me longer, but it was fun digging around to see what I could figure out. Either way I'm glad to hear that you're considering the idea. Thanks for all your work on bash. - emg
Bug using ] as index when declaring associative array
I recently tried to use ] as an index in an associative array. While it works fine after the array is declared, I could not figure out a way to include it in the initial declaration. To reproduce: declare -A aarr aarr[\]]=rbrac declare -p aarr # this should print a legal declaration for aarr eval $(declare -p aarr) # results in "bash: []]=rbrac: bad array subscript" -emg
IFS in function call in herestring for read
setup: foo() { printf "%q\n" "$IFS" >&2; printf "%s\n" "$*" >&2; } IFS=: read <<< "$(foo bar baz qux)" bash 4.2 output: : bar:baz:qux bash 4.3 output: : bar baz qux IFS is still set within the function call, but isn't being used in the expansion of "$*" -emg
Re: Does [ -f FILE ] have a bug on testing a symlink ?
from bash(1): Unless otherwise specified, primaries that operate on files follow symbolic links and operate on the target of the link, rather than the link itself. On Mon, Feb 9, 2015 at 1:00 PM, Cheng Rk wrote: > > > To bug-bash@gnu.org: > > > According this documentation `help test`, I am expecting it should return > false on anything other than a regular file, > > -f FILETrue if file exists and is a regular file. > > > but why it returned true on a symlink to a regular file? > > $ [ -f tmp/sym-link ] && echo true > true >
Usage of __P vs PARAMS in histfile.c
I tried compiling bash with musl-gcc and failed due to the __P macros in histfile.c While digging around it appears that __P is defined in stdc.h, but histfile.c indirectly includes rlstdc.h istead. rlstdc.h defines an identical macro with the name PARAMS instead of __P In my extraordinarily limited testing the switch from __P to PARAMS seems to work. That being said I'm not sure why the usage of __P worked with gcc but not musl-gcc yet, maybe someone more familiar with the code could pitch in and let me know if I've done something wrong. I've attached the patch against the devel branch HEAD. -emg From 4d533ebe10316dbb511088ecc92a4978ad3f0ad5 Mon Sep 17 00:00:00 2001 From: Evan Gates Date: Wed, 8 Apr 2015 08:17:11 -0700 Subject: [PATCH] change __P to PARAMS in histfile.c as far as I can tell histfile.c indirectly includes rlstdc.h not stdc.h both define similar macros the former as PARAMS and the latter as __P --- lib/readline/histfile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/readline/histfile.c b/lib/readline/histfile.c index 4c23785..a5af01e 100644 --- a/lib/readline/histfile.c +++ b/lib/readline/histfile.c @@ -124,9 +124,9 @@ int history_lines_written_to_file = 0; for more extensive tests. */ #define HIST_TIMESTAMP_START(s)(*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) ) -static char *history_backupfile __P((const char *)); -static int histfile_backup __P((const char *, const char *)); -static int histfile_restore __P((const char *, const char *)); +static char *history_backupfile PARAMS((const char *)); +static int histfile_backup PARAMS((const char *, const char *)); +static int histfile_restore PARAMS((const char *, const char *)); /* Return the string that should be used in the place of this filename. This only matters when you don't specify the -- 2.3.5
Re: logic in m4/strtoimax.m4 inverted
On Mon, Oct 3, 2022 at 12:48 PM Chet Ramey wrote: > > On 10/1/22 6:38 AM, Emanuel Haupt wrote: > > Static build of 5.2 fails to build on FreeBSD 13.1-RELEASE (amd64). > > > > The logic in m4/strtoimax.m4 is inverted. The following patch replaces > > strtoimax() if and only if it is present and functional. > > Thanks for the report. You're right, of course. Will you be applying this fix? I just ran into the same issue doing a static build with a musl toolchain.
[PATCH] prompt: add \m, PROMPTTIMEFORMAT, shopt prompt_time_all
Expand \m in prompts according to PROMPTTIMEFORMAT for the last timed command. This is an easy way to get the result of time without redirections by using @P expansions. Add shopt prompt_time_all to time every command run. Used in combination with \m the time of the previous command can be added to PS1, or every command in a script can be timed implicitly. --- I did my best to follow existing style, please let me know if I need to make changes. I also took a whack at updating the man page, but didn't touch the info page as I have no experience there. I went back and forth as to whether extra formatting options would be useful, similar to \D{format}. In the end I decided that keeping this as simple as possible for now was the best bet so I just reused everything from TIMEFORMAT. It is still possible to provide extra formatting, for example I'm using this in my bashrc: sec2hms() { local sec=${1%.*} fracsec min hr [[ $1 = *.* ]] && fracsec=.${1#*.} ((min = sec/60, sec = sec%60, hr = min/60, min = min%60)) printf '%d:%02d:%02d%s\n' "$hr" "$min" "$sec" "$fracsec" } ps1time() { local t='\m' t=$(sec2hms "${t@P}") t=${t##+([0:])} case $t in .000) t=0 ;; .*) t=${t##.+(0)}ms ;; *:*) t=${t%.*} ;; *)t=${t}s ;; esac printf '%s\n' "$t" } shopt -s prompt_time_all PROMPTTIMEFORMAT=%3R PROMPT_COMMAND='ct=$(ps1time)' PS1='[... $ct ...]\$ ' While writing I realized this couldn't just be on all the time. I tried turning it on only when \m was in a prompt, but this had a number of issues. Instead I settled on a new shopt for it, and was pleasantly surprised to realize the side effect was the ability to get the time from the last timed command without dealing with redirections. This then becomes handy even in scripts e.g.: PROMPTTIMEFORMAT=%3lR pt='Slept for \m seconds.' echo Capture time of last explicitly timed command. time sleep .1 echo interim command printf '%s\n' "${pt@P}" echo Or without printing the first time. TIMEFORMAT= time sleep .1 echo interim command printf '%s\n' "${pt@P}" echo Or time every command implicitly. shopt -s prompt_time_all sleep 0.2 printf '%s\n' "${pt@P}" sleep 0.3 printf '%s\n' "${pt@P}" And I'm sure someone smarter than I can figure out a fun way to use this with the DEBUG trap as well. builtins/shopt.def | 11 +++ command.h | 1 + doc/bash.1 | 23 +++ eval.c | 12 execute_cmd.c | 36 execute_cmd.h | 17 + parse.y| 11 +++ 7 files changed, 107 insertions(+), 4 deletions(-) diff --git a/builtins/shopt.def b/builtins/shopt.def index 1c7a3bd7..d601a4b0 100644 --- a/builtins/shopt.def +++ b/builtins/shopt.def @@ -99,6 +99,10 @@ extern int singlequote_translations; extern int patsub_replacement; extern int bash_source_fullpath; +#if defined (COMMAND_TIMING) +extern int prompt_time_all; +#endif + #if defined (EXTENDED_GLOB) extern int extended_glob; #endif @@ -258,6 +262,9 @@ static struct { # if defined (ALIAS) { "progcomp_alias", &progcomp_alias, (shopt_set_func_t *)NULL }, # endif +#endif +#if defined (COMMAND_TIMING) + { "prompt_time_all", &prompt_time_all, (shopt_set_func_t *)NULL }, #endif { "promptvars", &promptvars, (shopt_set_func_t *)NULL }, #if defined (RESTRICTED_SHELL) @@ -381,6 +388,10 @@ reset_shopt_options (void) singlequote_translations = 0; patsub_replacement = PATSUB_REPLACE_DEFAULT; +#if defined (COMMAND_TIMING) + prompt_time_all = 0; +#endif + #if defined (JOB_CONTROL) check_jobs_at_exit = 0; #endif diff --git a/command.h b/command.h index 1c068148..7e3dae1e 100644 --- a/command.h +++ b/command.h @@ -192,6 +192,7 @@ typedef struct element { #define CMD_LASTPIPE 0x2000 #define CMD_STDPATH0x4000 /* use standard path for command lookup */ #define CMD_TRY_OPTIMIZING 0x8000 /* try to optimize this simple command */ +#define CMD_TIME_NOPRINT0x1/* Don't print CMD_TIME_PIPELINE, prompt \m */ /* What a command looks like. */ typedef struct command { diff --git a/doc/bash.1 b/doc/bash.1 index cd4fd4ef..edd9dfd3 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -2663,6 +2663,18 @@ trailing directory components to retain when expanding the \fB\ew\fP and .B PROMPTING below). Characters removed are replaced with an ellipsis. .TP +.B PROMPTTIMEFORMAT +The format for \fB\\m\fP (see +.SM +.B PROMPTING +below) using the same format specifiers as the +.SM +.B TIMEFORMAT +variable. +If this varia
Re: [PATCH] prompt: add \m, PROMPTTIMEFORMAT, shopt prompt_time_all
Quoting Evan Gates (2024-09-16 19:03:21) > Expand \m in prompts according to PROMPTTIMEFORMAT for the last > timed command. This is an easy way to get the result of time without > redirections by using @P expansions. > > Add shopt prompt_time_all to time every command run. Used in combination > with \m the time of the previous command can be added to PS1, or every > command in a script can be timed implicitly. Any interest in this Chet? I just rebased on top of some new devel commits to build on another machine so it came to mind and I realized there was no response. I have quite enjoyed having the time of the last command in my prompt.