2
\$\begingroup\$

I'm working on a server and need a complete list of plugins that are installed for all wp installations. I only need to know whether a plugin exists or not, it doesn't matter where it exists.

For example, given:

/siteA/wp-content/plugins/someplugin
/siteA/wp-content/plugins/anotherplugin

/siteB/wp-content/plugins/someplugin

I need the following result:

someplugin
anotherplugin

Here is the working command:

for i in $(find . -type d -name 'plugins' | grep 'wp-content/plugins$'); do find ${i} -maxdepth 1 -type d -exec sh -c 'for f do basename -- "$f"; done' sh {} + ; done | sort -u

Can this command be shortened?

\$\endgroup\$
2
  • \$\begingroup\$ Doesn't work as expected for me. Output from for i in $(find . -type d -name 'plugins' | grep 'wp-content/plugins$'); do find ${i} -maxdepth 1 -type d -exec sh -c 'for f do basename -- "$f"; done' sh {} + ; done | sort -u is anotherplugin plugins someplugin. Plugins isn't requisite, right? \$\endgroup\$ Sep 3, 2019 at 9:22
  • \$\begingroup\$ No plugins isn't a requisite, it could be considered an error in the code as it's not actually a plugin but for my use case I didn't mind it being listed \$\endgroup\$
    – mrmadhat
    Sep 6, 2019 at 20:57

2 Answers 2

3
\$\begingroup\$

If plugins only contain directories, list every unique entry in plugins:

shopt -s globstar
\ls -- **/wp-content/plugins |sort -u

Otherwise, include the final targets in the glob, plus a trailing slash to limit globbing to directories.

That will yield paths relative to ., with trailing slash like siteA/wp-content/plugins/anotherplugin/. We clean it up with basename (shorter, easier) or awk (faster if there are very many results EDIT: nope, awk is slower in spite of the algorithmic efficiency).

The backslash ignores aliases, just in case an aliased ls would mess up the output.

shopt -s globstar

# probably easier to remember
\ls -d -- **/wp-content/plugins/*/ |xargs -n1 basename -- |sort -u

# slower, fancier
\ls -d -- **/wp-content/plugins/*/ |awk -F/ '!u[$(NF-1)]++ { print $(NF-1) }'
\$\endgroup\$
0
0
\$\begingroup\$

Note: a module is actually a directory located in some "wp-content/plugins" directories.

The main issue with your algorithm is the -exec primary. Contrary to what you think, the primary executes its argument for each corresponding file.

for i in $(find . -type d -name 'plugins' | grep 'wp-content/plugins$'); do
   find $i -maxdepth 1 -type d -exec basename -- '{}' \;
done | sort -u

Otherwise, the algorithm is correct but we have an issue about the performance. A good knowledge of utilities makes the difference.

For instance, your shell command operation looks like this:

> find <dir>
  >> find <dir A> <sub_dirs>
    >>> basename <dir 1>        
    >>> basename <dir 2>
    ...
  >> find <dir B> <sub_dirs>
    >>> basename <dir 1>
    >>> basename <dir 2>
    ...
  >> find ...
    >>> basename ...
    ...
> sort the output

Your may use find using the "AND" operator -a to filter the directory names.

find . -type d -path "*/wp-contents/plugins/*" -a \
    ! -path "*/wp-contents/plugins/*/*" -exec basename '{}' \; |
 sort -u
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I like the use of path but there would still be work to do after your command has run. Wouldn't the output contain duplicates and display the absolute path to each plugin? \$\endgroup\$
    – mrmadhat
    Sep 6, 2019 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.