0

This is related to How to execute a remote command and pass in local file as input?

I wrote a simple Java program that prints the first line of a file -

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.OutputStreamWriter;

public class ReadFirstLine 
{
    public static void main(String[] args) throws Exception 
    {
       String filename = args[0];

       BufferedReader iR  = new BufferedReader (new FileReader(filename));
       BufferedWriter oW = new BufferedWriter(new OutputStreamWriter(System.out));

       outputWriter.write(iR.readLine());

       iR.close();
       oW.close();
    }
}

Then, I created a symlink in the /usr/bin directory called ReadFirstLine which points to the /usr/local/RFL/ReadFirstLine script -

#! /bin/bash

java -cp "/usr/local/RFL" ReadFirstLine "$1" 

(/usr/local/RFL has ReadFirstLine.class)

Now, I can call ReadFirstLine from any directory like this -

$ ReadFirstLine simplefile.txt

This same script I want to call from a different machine. So from the remote machine I tried -

$ ssh username@xyz ReadFirstLine < localfile.txt  

However, I got the error

Exception in thread "main" java.io.FileNotFoundException:  (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at java.io.FileInputStream.<init>(FileInputStream.java:101)
    at java.io.FileReader.<init>(FileReader.java:58)
    at ReadFirstLine.main(ReadFirstLine.java:12)

How can I modify the Java Program / script such that this remote invocation works?

Wes
  • 831

4 Answers4

3

I don't know anything about Java, but I can show you a proof of concept. Say we have localfile.txt:

Here is the local file.

and on the remote machine, we have remote.sh:

#!/bin/bash
cat /dev/stdin

Note that the script on the remote machine invokes a program which reads from stdin. Then pass the contents of localfile.txt to your ssh command:

user@local:~$ cat localfile.txt | ssh user@remote remote.sh
Here is the local file.

Your Java program is trying to read a file which does not exist on the remote machine. I guess you could try to mimic a local file.

Change remote.sh to:

#!/bin/bash
cat "$@"

and invoke it with

user@local:~$ cat localfile.txt | ssh user@remote 'remote.sh <(cat /dev/stdin)'
Here is the local file.

I think it would be easier to change that part in your Java program to read from stdin.

I guess things might get messy if localfile.txt contains anything that might be interpreted by the shell as expandable (such as *), but that's for you to figure out.

  • Your addendum is strange because that exact syntax worked for me. – Wes Apr 14 '14 at 19:45
  • 1
    oh, yes, that's correct. I need to go to bed I guess. I was still using the modified version of remote.sh which expected a file descriptor, not stdin. It will work if you are reading from stdin. –  Apr 14 '14 at 20:02
2

The problem is that the shell redirection (<) sends the file over the ssh tunnel. And the Java class is expecting not the file, but a string with the "filename" of a local file that will be read with a FileReader.

Instead of passing the filename to the FileReader, read from the standard input.

InputStreamReader isReader = new InputStreamReader(System.in);
BufferedReader iR  = new BufferedReader (isReader);

Used this as a reference: https://stackoverflow.com/questions/5724646/how-to-pipe-input-to-java-program-with-bash

Now your class will be:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.InputStreamReader;

public class ReadFirstLine 
{
    public static void main(String[] args) throws Exception 
    {
       String filename = args[0];
       System.out.println(filename);

       //BufferedReader iR  = new BufferedReader (new FileReader(filename));
       InputStreamReader isReader = new InputStreamReader(System.in);
       BufferedReader iR  = new BufferedReader (isReader);

       BufferedWriter oW = new BufferedWriter(new OutputStreamWriter(System.out));

       //outputWriter.write(iR.readLine());
       System.out.println(iR.readLine());

       iR.close();
       oW.close();
    }
}

But for this task I would definitely use instead:

head -1 filename.txt

:)

  • Thanks. I wish I could accept your answer as well along with the one I accepted. – Wes Apr 14 '14 at 19:00
  • "ssh username@xyz ReadFirstLine < localfile.txt" works. I tested. No need to pipe the file to ssh. http://imgur.com/obzG292 – Roger Krolow Apr 15 '14 at 00:05
  • @user159841 sorry, I was very tired when I wrote that comment and was using the wrong test script. –  Apr 15 '14 at 04:51
0

The local file, by definition, is on your local machine. The java program is on a remote machine. The remote machine does not know about your local file, and in any production situation probably does not have permission to read it. I can think of three general approaches to this situation:

1) Change your Java program to read from stdin instead of from a file. Then your current syntax $ ssh username@xyz ReadFirstLine < localfile.txt will work.

2) scp your file from the local machine to the remote machine before running the java program. scp localfile.txt username@xyz Make sure you put it in the directory which will be the working directory for your java program.

3) Mount a remote directory in your remote machine to you local machine. This has the most setup, but is the solution that get closest to what you are asking for. It will allow you to make changes to the file locally, and the remote machine will see the changes. See this answer for more about mounting a remote direcotry.

https://stackoverflow.com/questions/232269/how-to-mount-a-linux-directory-from-a-different-pc-to-your-local-linux-pc

The argumante to the java program will be the path as it appears on the remote machine.

On local machine: /directory/which/has/been/shared/localfile.txt

On remote machine: /path/to/mounted/directory/on/remote/host/localfile.txt

$ ssh username@xyz ReadFirstLine < /path/to/mounted/directory/on/remote/host/localfile.txt
Tony
  • 224
  • 1
  • 5
0
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.OutputStreamWriter;

public class ReadFirstLine 
{
    public static void main(String[] args) throws Exception 
    {
       String filename = args[0];

       BufferedReader iR  = new BufferedReader (new FileReader(filename));
       BufferedWriter oW = new BufferedWriter(new OutputStreamWriter(System.out));

       outputWriter.write(iR.readLine());

       iR.close();
       oW.close();
    }
}
Jakuje
  • 21,357
saad
  • 1