0

today I wrote a script which should copy all files per ftp to my destination server.

Script is executed inside zsh-shell, so I guess I should use "sh script.sh" to execute it correctly?

Also I pasted my script to https://www.shellcheck.net/, which says its fine, but inside my zsh-shell I get following errors:

upload_file: command not found

#!/bin/bash

FTP_SERVER=ftp.ip FTP_USER=ftp.user FTP_PASS=ftp.passwd

FTP_DESTINATION_DIR="/destination_directory"

LOCAL_SOURCE_DIR="/home/$USER/pictures/local_pictures"

function upload_file { local file=$1 echo "Hochladen von Datei $file" ftp -inv "$FTP_SERVER" << EOF user $FTP_USER $FTP_PASS binary cd $FTP_DESTINATION_DIR put $file quit EOF }

find "$LOCAL_SOURCE_DIR" ( -name ".jpg" -o -name ".png" ) -exec bash -c 'upload_file "$0"' {} ;

I am not very used to this kind of scripting...

kekw
  • 143

3 Answers3

1

The issue here is that upload_file is a function that is available only in your script but not exported to the bash environment called by find.

To fix this, you can export the function using this:

typeset -fx upload_file

Put that before you call the find command, and it should work as intended.


Edit: tripleee pointed out in the comments that export -f could also be used. Which is arguably much more readable.

1

It looks that you are not declaring function properly. Assuming from the output, and behavior you are missing parentheses function upload_file {... should be function upload_file (){... (after you edited the question that is not a problem anymore I assume).
In bash you should export the function so it is visible when you execute bash -c in find step:

export -f upload_file
find "$LOCAL_SOURCE_DIR"...

As you can see in this answer.

dexter
  • 46
1

The immediate problem is that the function you declare is not visible to the second bash instance you run from find ... -exec. https://shellcheck.net/ probably just sees a single-quoted literal string, and does not look inside it for problems.

Using sh to run a Bash script could also be a problem. Your script doesn't use any Bash features other than the nonstandard function declaration, but there is really no reason to prefer that over the standard POSIX function declaration syntax. (If sh is a symlink to Bash on your system, it probably works even though many Bash-only features will be turned off when it is invoked as sh. But you shoud still understand the difference, and use bash to run Bash scripts.)

Anyway, the preferred mechanism is to chmod +x the script, and then simply run it by specifying its path (or just its name, if the directory it's in is on your PATH). This allows the shebang to select the correct interpreter. (It's simply ignored as a comment if you explicitly run the script with sh or bash, or even something completely wrong like python or csh).

With these details fixed, your script would look like this.

#!/bin/bash

FTP_SERVER=ftp.ip FTP_USER=ftp.user FTP_PASS=ftp.passwd

FTP_DESTINATION_DIR="/destination_directory"

LOCAL_SOURCE_DIR="/home/$USER/pictures/local_pictures"

upload_file () { local file=$1 echo "Hochladen von Datei $file" ftp -inv "$FTP_SERVER" <<EOF user $FTP_USER $FTP_PASS binary cd $FTP_DESTINATION_DIR put $file quit EOF }

export -f upload_file

find "$LOCAL_SOURCE_DIR" ( -name ".jpg" -o -name ".png" ) -exec bash -c 'upload_file "$0"' {} ;

A better fix still would be to allow upload_file to accept multiple file names, and only create one FTP session.

:
upload_file () {
  ( printf '%s\n' \
      "user $FTP_USER $FTP_PASS" \
      "binary" \
      "cd $FTP_DESTINATION_DIR"
    local file
    for file; do
        # print diagnostics to stderr
        echo "Hochladen von Datei $file" >&2
        echo "put $file"
    done
    echo "quit" ) |
  ftp -inv "$FTP_SERVER"
}

export -f upload_file

find "$LOCAL_SOURCE_DIR" ( -name ".jpg" -o -name ".png" )
-exec bash -c 'upload_file "$@"' _ {} +

Tangentially, probably don't use an .sh extension on your shell scripts, especially if they are not actually sh scripts. There is no strong convention around this, but ls doesn't have an extension, either; why should it?

tripleee
  • 7,699
  • 1
    I guess the round parentheses in the body of the updated function could be replaced with curlies to avoid spawning a subshell, but I'm not in a place where I can test that. You'll need a newline or a semicolon before the closing curly then. – tripleee Dec 29 '22 at 15:17
  • unfortunately I can not establish connection to ftp host :/

    If i connect manually with ftp server_ip and insert my username and passwd it works, but I need to do it in one action :/

    Loop is correct and find all occurences, in my case all pictures, but establishing a connection to ftp always fails..

    – kekw Dec 29 '22 at 16:28
  • The stock ftp client may not be properly scriptable. Sometimes you can work around this with a strategically placed sleep or etc but ultimately in this scenario the robust solution is to use a client with scripting support (it's been a while, but I recall using lftp and ncftp back in the day) or move to a scripting language like Python, whose standard ftplib module should be straightforward to figure out. – tripleee Dec 29 '22 at 16:34
  • alright, python is always an acceptable solution for me! thanks for this advices! :) – kekw Dec 29 '22 at 16:35