coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 'Cat' feature request


From: Michael Cook
Subject: Re: 'Cat' feature request
Date: Wed, 27 Dec 2023 14:20:20 -0500

>
> If Bash had macros, we could do things like this in better ways.
> Imagine we could do this:


A Bash function could use `history 1` to see exactly what was typed on the
command line.
And then do its own parsing.

function foo {
    bar "$(history 1)"
}

That's a little hacky because Bash is going to do its own parsing of the
line regardless. So the line still needs to make sense to Bash first (e.g.,
balanced quotes).


On Mon, Dec 25, 2023 at 10:53 PM Kaz Kylheku <kaz@kylheku.com> wrote:

> On 2023-12-25 16:58, Pádraig Brady wrote:
> > On 25/12/2023 21:25, Kaz Kylheku wrote:
> >> On 2023-12-22 10:09, Evan Tremblay wrote:
> >>> so, if you run:
> >>> cat *
> >>>
> >>> it wont run(unless you use --show-all).
> >>
> >> Are scrambling to win a stupidest post of 2023 contest or something?
> >
> > That is not an appropriate response.
>
> Sorry, Christmas is a little touch-and-go so details fall by
> the wayside. I now have a moment to explain why it deserves
> being called stupid.
>
> It is not because of the evaluation strategy of * being
> expanded by the shell.
>
> The feature is actually implementable. The cat program has a way of
> determining that it has been passed all the names that may arise
> from the expansion of *. (Modulo a minor sampling-related race
> condition.) Namely, it can just call glob("*", ...) and compare
> the results to its argument vector.
>
> It's a complete nonstarter because:
>
> 1. Unknown numbers of scripts out there depend on "cat *" just
>    working. If an option is suddenly required to make that
>    work, those scripts break. A legitimate use might look
>    like, oh:
>
>      # get the contents of all files in target_dir into file
>      (cd $target_dir; cat * > $target_contents)
>
> 2. In relation to 1, such an option is incompatible with other
>    implementations of cat (including prior versions of the
>    same Coreutils one) and, importantly, with the POSIX standard.
>    POSIX prohibits an implementation of cat from requiring
>    an opt-in option in order for "cat *" to do what it is told.
>
> It's possible to have it as an opt-in behavior, and that would
> also eliminate the inefficiency (from regular use):
> cat --not-all-files could do the expansion of *, and fail if
> all the files in that expansion are present in the command line.
>
> It's technically objectionable to include such hacks in the
> core utilities.
>
> Presumably, this protects the interactive user from accidentally
> catting all the files in a large directory.
>
> A protective feature in the interactive environment can be
> obtained by writing a shell function in Bash.
>
> Shell functions in people's personal, private environments *can*
> be ugly hacks. All that matters is whether they are acceptable
> to that individual.
>
> Here is a basic crack at it:
>
> cat()
> {
>   local -a orig_args=("$@")
>   local -a star_files=(*)
>   local all_present=y
>   local occurs_not
>   local i
>   local j
>
>   # Crudely skip arguments that look like options
>   while true; do
>     case "$1" in
>       -* | --* ) shift ;;
>       * ) break ;;
>     esac
>   done
>
>   # get remaining args into args array
>   local -a args=("$@")
>
>   # determine whether all files in star_files are in orig_args
>   for i in "${star_files[@]}"; do
>     occurs_not=y
>     for j in "${args[@]}"; do
>       if [ "$i" = "$j" ]; then
>         occurs_not=
>       fi
>     done
>     if [ $occurs_not ]; then
>       all_present=
>       break
>     fi
>   done
>
>   if [ $all_present ] ; then
>     echo "cat: all files that match * present in command line!"
>     return 1
>   fi
>
>   command cat "${orig_args[@]}"
> }
>
> This has O(M * N) behavior, in the number of file arguments M and
> number of files that match the * pattern, N.
>
> This is kind of a "feature". When you do "cat file.txt", you hardly notice
> a slowdown; it doesn't take much time to compare that one file
> to thousands of files.
>
> But when you type "cat *" in a large directory, it will just sit there
> for a while, and that alone alerts the user that the very thing they
> are trying to avoid has happened. Rather than waiting for the inevitable
> diagnostic, they can just hit Ctrl-C.
>
> There are ways to speed it up by taking advantage of the contents of
> the * expansions being sorted. We change the requirements to this:
> we look for situations when the command line contains, as a contiguous
> subsequence, the sequence produced by *.
>
> If Bash had macros, we could do things like this in better ways.
> Imagine we could do this:
>
>   macro cat()
>   {
>   }
>
> where "cat * *.txt $X" receives three arguments that are literally '*',
> '*.txt' and '$X', with no expansion having taken place, only a division
> into fields. It could then check that '*' does not occur, and
> then use eval to execute command cat "$@".
>
>
>
>


reply via email to

[Prev in Thread] Current Thread [Next in Thread]