Home > Software design >  Composer show output is different if echoed or redirected to a file
Composer show output is different if echoed or redirected to a file

Time:09-08

I try to make a .sh file that list all my no-dev package and then copy them to an other repository. So I come with this code :

#!/bin/sh
list_of_all_no_dev_package=$(composer show --no-dev --name-only)
for no_dev_package in $list_of_all_no_dev_package; do
  cp -r "vendor/${no_dev_package}" "${export_path}/vendor/${no_dev_package}"
done

To ensure my cp command is fine, I tried to echo it in my terminal and got something like

cp -r "vendor/mypackage" "myexportpath/vendor/mypackage"

However, if I try to redirect the output to a text file with the >> operator, I got something very different

cp -r "vendor/https://github.com/mypackage/tree/master/whateverfile.php" "myexportpath/vendor/https://github.com/mypackage/tree/master/whateverfile.php

How can I get the first output with a composer command and why are those 2 results so different?

CodePudding user response:

If simple command substitution does not work right out of the box for you (which can happen), I'd consider - next to improving shell debugging skills - to get composers output more stable.

The one part I already placed as a comment, in shell scripts the following global arguments:

  1. use -n to make composer non-interactive if that is the intended use. this keeps many issues out of the factory.
  2. use --no-plugins to disable plugins, you want to keep those out as well in scripts.

In addition to those run-time-behaviour controlling arguments there is also one for the output. This is helpful if you want to ensure it is machine-readable. For example you can get the installed package names as JSON Text:

$ composer -n --no-plugins --format=json show --no-dev --name-only
{
    "installed": [
        {
            "name": "composer/ca-bundle"
        },
        {
            "name": "composer/metadata-minifier"
        },
        ...
    ]
}

You can then use a utility to pipe the result into that is able to parse JSON Text and error out otherwise, e.g. jq(1) or php(1). Here an example w/ PHP (7.0 ) as it is likely already available:

$ composer -n --no-plugins --format=json show --no-dev --name-only \
| php -r \
'  echo implode("\n", array_column('\
'    json_decode(stream_get_contents(STDIN))->installed, '\
'    "name")) ?? exit(1), "\n";'
composer/ca-bundle
composer/metadata-minifier
...

This then is a good filter to further process the data in your script.

Do a strict validation of the composer.json before running any of such scripts as here you want to ensure that all package names are strictly conforming with the portable composer package name profile that is now in effect since a couple of releases (but I have no idea how deep it scans, otherwise pathchk(1) comes to mind).

Full example with some shell debugging and error handling:

#! /bin/sh
set -x

set -e
list_of_all_no_dev_package=$(
  composer -nq --no-plugins validate --strict >&2 &&
    composer -n --no-plugins --format=json show --no-dev --name-only |
      php -r 'echo
          implode($terminator = "\n",
              array_column(
                  json_decode(stream_get_contents(STDIN))->installed,
                  "name"
              )
          ) ?? exit(1),
          $terminator
        ;
      '
)
set  e

for no_dev_package in $list_of_all_no_dev_package; do
  printf "no dev package: '%s'\n" "${no_dev_package}" >&2
  cp -r "vendor/${no_dev_package}" \
        "${export_path?:export_path unset}/vendor/${no_dev_package}"
done

And if you consider to actually use /bin/bash check your bash version, there are arrays and you can read them in, compare shellharden etc..

CodePudding user response:

Problem seems to occur on recent composer installation. It does happend on actual latest : 2.4.1 but does not on 2.1.2

Using composer show --name-only actually return a result with the full repository path (github or whatever has been set up in the config files) and add 2 ESC char in it. The CLI then prints what's between those 2 ESC char.

Solution is to add the global option --no-ansi

composer show --name-only --no-dev --no-ansi will return the directory name of the repository in your vendor folder.

  • Related