#!/bin/bash # Synology Universal Search CLI (Command line interface) # # Command line tool to execute Synology "Universal Search" from within a shell. # # The web-gui on a synology NAS has a thing called "Universal Search". I wanted to be able to use this # from the shell command line on the NAS also, and therefore created this script. # # Requirements: # # - A configured universal search (via the web-gui), which has indexed the folders to be searched. # - Root shell access to install the search. # # Installation: # # - Log on as root to a command line shell (login as admin user, and type "sudo -i" to get to the root shell). # - Run "wget https://raw.githubusercontent.com/atkaper/synology-universal-search-cli/main/search -O /tmp/search" # - Optional: Look at the script, before trusting to run it: "cat /tmp/search" # - Run the downloaded script using "bash /tmp/search --install". # - Run "rm -rf /tmp/search" # # The above install will copy the script to /usr/local/bin/search, and setup sudo rights to execute it. # The sudo rights are needed for "normal" non-root users to be able to execute "synowebapi", which is used in this script. # Note: an attempt has been made to make this somewhat safe, but keep in mind that this script runs as root, so # if there are any loopholes to let it execute any other sub commands, not included in the script, then you # will expose root access to your logged on users. Let me know if you find any security holes ;-) # I'm not too bothered by this, as I'm the only shell user on my NAS. # # Note: if a synology OS update is done, you could lose the passwordless sudo setup. To fix this, just run # "search --install" to re-install the sudo configuration. # # Note: if you do NOT want to "install" the script for all users, you can also just copy it manually to /usr/local/bin, # and always run it with: "sudo search [searchterm]". # # Thijs Kaper, May 7, 2021. # Some safety settings set -euf -o pipefail # Max nr of results to show MAXHITS=1000 # Check if any parameters passed if [ "$*" == "" ] then echo "usage: $0 [searchterm] [searchterm] [searchterm] ..." echo "To prevent your shell from handling special characters, you may surround the search term with quotes." echo "A universal search will be done, just as when used from the synology web-gui. Max $MAXHITS results will be shown." exit 1 fi if [ "$UID" != "0" ] then # need root to run the script (for use of synowebapi, or for install) exec sudo /usr/local/bin/search "$@" fi # We are running as root, check for install. if [ "$*" == "--install" ] then echo "Attempting installation of search function for use by all users" if [ ! -f /usr/local/bin/search ] then cp -av "$0" /usr/local/bin/search || exit 1 fi chmod -v 755 /usr/local/bin/search || exit 1 chown -v root:root /usr/local/bin/search || exit 1 if ! grep -q /usr/local/bin/search /etc/sudoers then echo "Adding sudoers permissions for /usr/local/bin/search" ( echo ; echo "%users ALL=(ALL) NOPASSWD: /usr/local/bin/search" ) >>/etc/sudoers || exit 1 fi echo "Installation for all users done as /usr/local/bin/search" exit fi # Determine grep filter to use at end, to highlight search terms, filter out lots of stuff to prevent grep (execution or security) issues. # Convert remaining separate words to have " -e " between them, for use by grep. FILTER="$(echo "$*" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9 ]/ /g;s/ \+/ /g;s/^ //g;s/ $//g;s/ / -e /g')" if [ "$FILTER" == "" ] then # empty not posible (because the grep prefixes it with "-e"), so just add dummy start of line match FILTER="^" fi # Create temporary output file umask 0077 DATA="$(mktemp)" # Note: if you use the web-gui, and open your F12/network tab in your browser, you can try a search, and look at the used attributes in the POST, if you want to tweak aynything in this search. synowebapi --exec api=SYNO.Finder.FileIndexing.Search method=search version=1 search_weight_list='[{"field":"SYNOMDWildcard","weight":1},{"field":"SYNOMDTextContent","weight":1},{"field":"SYNOMDSearchFileName","weight":8.5,"trailing_wildcard":true}]' keyword="\"$*\"" size="$MAXHITS" from="0" fields='["SYNOMDSharePath","SYNOMDFSName","SYNOMDFSSize","SYNOMDIsDir","SYNOMDContentModificationDate"]' sorter_field='"relevance"' sorter_direction='"asc"' sorter_use_nature_sort="false" sorter_show_directory_first="true" > "$DATA" 2>/dev/null # Format output with timestamp, dir/file flag, path, and size, and use grep to highlight search terms (the "-e ^" is to match ALL lines, in case FILTER does not match) jq -r '.data.hits[] | ( (.SYNOMDContentModificationDate|tonumber | strftime("%Y-%m-%d %H:%M")) + (if .SYNOMDIsDir=="y" then " [dir] " else " [file]" end) + " \"" + .SYNOMDPath + "\"" + (if .SYNOMDIsDir=="y" then "" else " (" + (if .SYNOMDFSSize|tonumber >= 1048576 then ((.SYNOMDFSSize|tonumber)/1048576+0.999|floor|tostring) + "MB)" else .SYNOMDFSSize + " Bytes)" end) end) )' "$DATA" | grep --color -i -e ^ -e $FILTER || true # Show total result count (can be higher than the max $MAXHITS we return). echo -n "Total results:" jq -r '.data.total' "$DATA" # cleanup temp file rm -rf "$DATA"