Spawn External Processes with Rust Standard Library πŸ¦€ Rust Programming Tutorial for Developers

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys my name is Trevor Sullivan and welcome back to my video channel thanks so much for joining me for another video in our rust programming tutorial series we're above 30 videos now thanks so much for all of your support and I hope that you'll enjoy this video as well the topic that we're going to be focusing on in this video which you probably ascertained from the title is running external commands or external processes from a rust application now now you might ask yourself well if I can write rust why wouldn't I just build the functionality directly into my rust application rather than calling an external binary after all rust is high performance so wouldn't I just want to write that functionality directly into my application instead of slowing down my entire application by relying on some kind of external system well in general that's true but there are some situations where you probably want to consider simply calling out to an external binary to give you a little context let's say that for example a vendor gives you a binary you don't have the source code for it you don't have any specifications for what that binary is doing or the file formats or data formats that it's working with across the network and so you don't really know how to take that reverse engineer it and then build that functionality into your rust application right so that's one example where you have proprietary software that you can't reverse engineer another situation could be that maybe the functionality that you're trying to get after is simply too complex and it would require too much time in order for you to again understand those specifications and figure out how to build your own and thoroughly test your own library for that particular purpose one great example of that could be something like FFM peg this is a very powerful utility it's kind of a Swiss army knife for audio and visual tasks that allows you to do things like convert between different f F formats or maybe re-encode a video under a new bit rate if you want to maybe optimize it for video streaming online you can re-encode audio as well you can kind of slice and dice your files you can apply different filters to audio and video to do things like noise reduction in video or even uh noise reduction in audio as well so this is a huge utility there's tons of kind of his history that goes into the FFM Peg utility and if there's some kind of advanced function you need to do with audio or video you're probably just better off using FFM Peg rather than trying to build that functionality yourself unless that's your specific goal is to literally replicate what FFM Peg does in Rust but in a lot of cases if you're just trying to maybe build a security system solution or do some backend video conversion maybe you're building your own private version of YouTube or something like that then just building on top of the existing FFM Peg utility is going to be most likely the fastest way for you to achieve your ultimate objective so there are going to be a lot of situations where we just want to use rust in order to call out to an external binary and offload that functionality to some kind of external task now you don't want to do this by habit necessarily you don't want to use it as a crutch too often but for a lot of advanced functionality or anything that is kind of a blackbox and a proprietary then it kind of makes sense to allow rust to call external programs right something else you might want to do is integrate with external systems that you don't have control over and for example maybe other developers have built their applications in nodejs or python or Java or C or Powershell or maybe somebody wrote A bash script and so you need to go off and kick off a bash script or kick off a Powershell script or you know kick off a python script or module that somebody's developed rather than replicating all that functionality in your r program so part of the standard crate in Rust actually provides a subm module here there's a whole bunch of subm modules and we've already looked at some of these but one of these subm modules in the standard crate is just called process right here and this is a module that allows us to work with external processes so we can kick off processes and we can actually customize the invocation of those external processes as well as we'll see on during a Hands-On demo a little bit later on in this video but just to give you a brief overview of the kind of entry point into this module so we've got a bunch of data structures here we've got a termination trait here we've got a few functions that are helpers as well but the primary entry point into this module is going to be this command type right here so this is a data structure that you can instantiate by calling the new function right down here and then the input to this command structure when you call the new function on the type is going to ask you for the program or the binary path that you want to execute so all you'll do is you'll pass In Like A bash or python or maybe Python 3 if you don't have a SIM link for python to point to Python 3 uh could be node it could be bun or some other runtime like that so this is your opportunity to specify what program you want to execute it could even be something that you typically call from a bash shell like an echo command or a a head command or a or grep or that kind of thing so you can really call any program that you want to with this data structure here now there's a lot of customization you can apply when you actually kick off one of these programs you'll see in a really simple example down here they're just kicking off a sh shell but if you want to pass in command line arguments to modify the behavior of the process that you are spawning then you can use this ARG function here there's actually a few different functions here deal dealing with args there's ARG that allows you to add in a single argument if we take a look at args here we can pass in a collection of additional arguments as a slice right here and then we can also do things like customize environment variables so we can use this EnV function right here to tack on an environment variable before we ever actually spawn the command itself and we can also do things like set the current working directory so if there's a certain file system folder that contains some supporting files for the process that you're trying to run you can use this current der function to configure the command object to change its working directory to a different path before you actually spawn the process so those are a few customizations you can apply but in order to actually kick off the process that you've configured so again after you've called new here to specify the program after you've called ARG or args to to add any arguments add environment variables set the working directory then we actually need to spawn or execute the process and the way that we do that is with a couple of different functions here one function is synchronous meaning that it'll kick off the process and pause execution of your rust program until the process has actually completed and that's called the output function right here as you can see output is going to execute the specified command along with its configuration like the working directory and environment variables and add additional command line arguments as a child process so in a process tree it's going to be a child process of your rust program it'll wait for it to finish and then it'll collect all of its output so how do you get the output from the program well as you probably know processes on most systems have three different streams that you can use we have standard input which allows us to send input data into the program using a pipe piping or streaming mechanism and then there's two different types of output that we can retrieve from a program that has executed or is in the process of executing and that's the standard out that's the most common and then you've also got one called standard error that you can use to get any kind of error messages from a program assuming that the program specifically wrote error messages to standard error so in order to retrieve those we can reference these functions right down here standard error standard in and then standard output and those will give us access to the input handles so we can pass data into the application as well as grabbing error and output from the program as well and then what's really cool is that using the standard input handle we can actually spawn a new process and take the output from one program and actually pipe the output from one program into another program very similar to what you might do in a bash shell or a poers shell Shell Shell where you just take a command you spit some output and then you use the pipe character the uh vertical pipe character if you do shift backslash on your keyboard and then you can pipe that data output from one command into a different command so we can achieve that same type of system without having to use bash and actually using rust instead so we're going to be taking a look at how to use this command type here inside of the process module which again is part of the standard crate you don't have to install any additional crates from the crates.io registry this is all built into the standard Library here so we're going to spin up an example of this and take a look at how this different functionality works but before we get into the code sample here I just wanted to invite you to subscribe to my YouTube channel youtube.com/ Trevor Sullivan I'm an independent content creator so any kind of support that you can provide to this channel is incredibly helpful so I've got a lot of different videos here a lot of them are on the rust programming language so you can check out this playlist here called rust programming tutorial and we're up to over 30 videos at this point and we cover a lot of rust fundamentals so we do control flow um you know match Expressions the option en type the generics functionality Dynamic dispatch threading asynchronous operations and all that fun stuff so check out this playlist also leave a thumbs up or like on the video If you learned something new from this video and also make sure that you leave a comment down below and let me know what your thoughts are and any other kind of rust topics that you want to see also I'm going to leave a pinned comment down below with a link to my Amazon shop here and I've actually gotten accepted into this program here where you can actually just go to my landing page which will be linked in the comment below it's amazon.com shop Trevor Sullivan and if you purchase from the store here this directly goes towards helping to support the channel so I can bring you more videos like this one so please consider lending your support to the channel here and check back regularly for new videos all right so we're going to go ahead and actually Implement some code here so I'm going to switch over to my vs code editor here and as I demonstrated way back in my first video in the rust series I am working on a remote Linux virtual machine here it's running on one of my bare metal servers running a boont server and then I just use Lex D which I have a separate video series on to create development workstations so that I can rapidly rebuild Dev environments and have you know different virtual machines for different internal services or different programming languages if I want to so I've got a virtual machine that I'm currently connected to thanks to the uh vs code remote extension here that allows me to just SSH into my Linux box so even though I'm running Windows locally here I'm remotely connected to my Linux server and if I just do un name- a or LSB release-- all you can see I'm running auntu lunar Lobster which is the latest release of auntu Linux in any case what we're going to do is just create a new project folder for us to work inside of and then we'll start taking a look at that process type so let's just do a maker and we'll say external process and then we'll do contrl K control o and open up that directory in our editor here then the first thing that we typically do when we spin up a new rest project is we go back into the terminal inside of our project directory which is totally empty if I do ls- LGA you can see there's nothing in there no hidden files even so we'll just do Cargo in it to create a new binary and now we can open up our source folder and then go to main. RS and this main function is our entry point into our application also I'm going to switch on Z Zen mode here uh that just gives us a little bit more screen real estate to dedicate to the actual code all right so the first thing we'll do here is just eliminate our print line statement here and the first thing we want to do is actually construct an instance of the command data structure so there's a couple of different ways you can do that you can either just do STD and then go to process as a subm module and then do double colon and say command then we can do a double colon and call this new function and then we can pass in the application that we want to launch so we could say like node if we want to launch a nodejs project we could do Python 3 if we wanted to run a python script we could do pwsh if we want to run a Powers shell script we could call bash to call A bash script uh if we wanted to search for a binary on the systems a path environment variable we could use the witch command in order to locate a a binary so if we want to check if node exists if it's been installed on the system or check if Python's been installed on the system before we actually attempt to execute it we could use the witch command and I will mention we're not going to use this but if you head out to the crates.io registry here there is actually I noticed a third party crate called witch right here that's been installed 67 million plus times so it's obviously pretty popular here uh but this actually allows you to accomplish the same thing that the witch CLI command does so if you'd rather take a rust approach to checking if a particular binary exists on the path environment variable then this is a more rustic way of handling that rather than calling which from the command line but the whole purpose of this is to kind of demonstrate how you can use CLI tools so in this case we don't necessarily care about being rustic right so we could do this or we could say I want to use the command type up here so this allows us to import the command struct into our program and that way we don't have to specify this full module ual instruct path in order for us to reference that so once we've specified this Ed statement now we can just say command new and that'll instantiate a new command all right so now we need to assign the result of this command to a variable now when you construct the command instance you're not actually spawning the process remember that there's an output function and I think I forgot to mention that there's also a spawn function down here as well so that's the second function that you can use in order to invoke the actual process the key difference with spawn though is that it returns a handle to the process so that you can actually inspect the status of the process to see if it's still running or if it has exited and then you can inspect the exit code of the process but if you use the spawn function it's asynchronous and it allows you to do other stuff inside of your rust code while periodically checking the status of the process to determine if it has exited or not and if it has what exit code it exited with so if you need to spawn a process and then continue doing some work in your rust program and sometime later in your program you'll check that process handle to see the status and make sure that it's finished and then grab the output from the program you can do that with spawn instead of output but it's up to you which approach you want to take all right so we're going to assign this to a variable so I'll say P1 as a variable name and that'll just give us this command object and if we wanted to pass in an argument here so typically at the command line here we might say which python right and if python can't be found on the path environment variable then the dollar question mark variable here in bash indicates that we have an exit code of one instead of zero and zero is the standard exit code for processes if they're successful so if you get a nonzero code like one or 253 or something like that then that is going to indicate that there was some kind of issue executing that command and so in this case when we get a a number one exit code from the witch command that means that python doesn't exist on our path but if I do which three instead you can see that we get the path returned to the Python 3 binary so if we do e Echo dollar question mark now you can see that we just get back a zero exit code so anytime in Rust that we're calling external programs we want to make sure that we look at the status exit code of the process to make sure that it's zero and if it's not zero then we want to be able to handle that properly in our rust code but in this case we need to pass in some program that we're looking for on path as an input argument to which so which is the binary we want to call but then in order to tack on that second argument what we do is say ARG and then we just pass in a string value here just a uh borrowed string Slice on the stack and we could say Python 3 because I already know that exists we'll just use that one for success for now and then after we configure that argument we can just do p1. output and that'll return a standard result object to us wrapping the output as well as an error if that occurs so I'll say let proc results equal p1. output and then we'll say if proc result dot is okay so that'll just check to see if we have output or if an error was thrown but if it was successful if it does provide a result then we want to be able to retrieve the output from that result object so we'll do proc result. okay because we already checked to see if it is okay and then we'll go ahead and unwrap the value from that and assign it to a variable so let's do let result equal the unwrap there and now we just get direct access to the output here so now we can do results and check for the different things that we can do with this and right down here you'll see that we have the ability to grab the status we have Standard air we have standard output and so we can use these to kind of examine the status of the process so let's do result. status and that'll give us this exit status object right here and if we wanted to check out the documentation for that type we could come back to the process module here so you can just click on the process in the breadcrumbs there go to data structures and then we have exit status here and then exit status has a few different functions or methods on the struct instance called code so that'll give us an option wrapping the return code here we also have exit Okay this is actually a nightly only API so don't use that one unless you're on the nightly version of rust and we also have success here and success is just a helper function that basically checks for a zero exit code so if it's zero then it's true it was successful or it'll return false as a Boolean value if it's a nonzero code so anything from 1 to 254 so we can go into the status here and then we'll grab dot let me go back into zen mode here then we'll say dot and say success and then we'll pass that into a simple print line statement here and we'll just say was execution successful and then we should get either a true or false so let's do a cargo run and test out our program here and the other thing I need to do here is if you get this error saying temporary value dropped while being borrowed then you can just terminate your command new function here and then after that you can do p1. ARG Python 3 and we also need to make that mutable as well so we need a mutable reference so that we can change the arguments change the environment variables change the working directory and any other configuration options of the process and so then that should resolve that issue with borrowing while uh while referenced and so now you can see was execution successful true yes it was because we got a zero exit code because which Python 3 should work fine now if we change this to which python I don't have a SIM link that points python to Python 3 as we saw earlier in the bash shell here so we get a one exit code so if we just do cargo run again you should see false instead of true because now we got a nonzero exit code and that success function that we are calling right here on the exit status struct is giving us false instead but we could also get the specific exit code so we could say if result. status. success is false so we'll say if not success then we'll say error occurred and we'll grab the exit code by saying result. status. code and then that should provide the underlying status again it's w wrapped in an option here so we'll do dot unwrap here to get the underlying value of that option wrapping the I 32 so now when we run this you can see we get error occurred and the exit code is one which is the exact same as if we ran it in bash and echoed out the exit code now what if it was successful so let's switch back to a successful command like doing which Python 3 and let's say that we actually wanted to get the standard output right so if we do which Python 3 in our bash shell here you can see that the witch program actually outputs the full file system path to that specific binary that was located somewhere on the path environment variable right and that looks like this here for anybody who's relatively new to technology here it's basically just a colon delimited series of array of uh folders and so basically which is searching all these folders to see if it can find a binary with this name or an executable with this name and if it can then it'll return the full path to it whichever folder it ends up locating it inside of so as you can see we should have user bin in our path right here so it checked all of these earlier folders and couldn't find it but then when it got to user bin it said oh I see that Python 3 is inside of there so I'm just going to give you user bin and then the name of Python 3 so how do we get this output from the witch command in our rust program so let's say we wanted to search for a binary and if we get a zero exit code so if it's successful in locating that binary then we want to execute that binary right so this is where standard out comes into play so we've looked at the status of the process to get the success or failure of it and we've also gotten the specific exit code of the process that's returned back to the operating system but we want to grab the actual results of the process so in this case we're going to go inside of this if block here because we want to make sure that there is output and there there wasn't some kind of error attempting to spawn the process and we also want to kind of skip over this block right here because if an error occurred while trying to locate that binary on our path then we don't want to try to execute that binary or we don't want to try to retrieve the standard output from that command because it's not going to Output anything so what we'll do is do an else statement here so what we're saying if it was not successful then print out the error but if it was successful then we want to grab the standard output so we can see what the path is that it's telling us to execute so what we'll do is uh plug in a print line statement here and then we're going to pass in the standard out and it's a little bit convoluted to get that what we need to do is go to the process we'll go to process module and then go to the uh command object right down here and then after we get the result back from it let's go to Output we'll have a result of output and so the data structure here that we're ultimately inspecting is this output type here this is what we're unwrapping on line number 10 right here in order to uh get the results. status that status field is on this output type here but we also have standard out and Standard air fields on this result as well in addition to status so down here we're going to do result. standard out this time because we want to grab the output of the program and when we grab this property here or this field from that data structure it's going to give us a VEC of u8 or unsigned 8bit integers as a VC type and I actually have a separate video earlier in the rust series that specifically talks about how to work with VCS but it's easiest to just kind of uh visualize it mentally as an array of unsigned 8bit integers and that's because if you think about it a program could actually return binary data not not necessarily text that somebody can read in in a language but it could just be a random binary data from maybe a compiled program or just some random data that was written to a file right so in order for us to turn this into the output that we get from bash where we actually get the path to the binary we need to take those unsigned 8bit integers and translate it into actual readable text right and so the way that we do that is by grabbing the VEC of u8 and using the string type so we'll do string from and then we'll interpret it as utf8 so utf8 is a common text encoding that's used to encode a lot of different languages across the world and then all we have to do is pass in our VEC of u8 and this function will return a string to us which can in turn be passed into the print line macro so that we can display that to our terminal so let's pass in our result. standardout into this function also the from utf8 function here is going to return a result of its own that either contains an error turning it into utf8 if it's invalid utf8 or it'll give us the final resulting string so we'll just say do okay here or actually unwrap and we're just going to assume that it's going to be successful for now we're not going to do proper error handling as we typically would but now if we run our program with cargo run right here you can see that that'll print out the file system path on our Linux system here to the Python 3 binary so we could change that to something else like which pwsh so I'm basically asking the system using rust code here do I have Powershell installed and if so what is the path to it and if I run that you can see that we don't have it installed so we got an exit code of one so if I do a snap install Powershell D- classic to install the Powershell runtime onto my Linux system here and try to rerun my program then we should get a successful result so we'll just give that a second to download Powershell and then we'll go ahead and rerun this all right so we've taken a look at how to call the output function and again this is going to pause and return control back to your uh rust program after the process is completed but we can also do the spawn function so we'll take a look at that next and see how to asynchronously invoke our Command here so if we go down to spawn right here you can see that it returns a handle to the new child process that is spawned so we'll take a look at that in just a moment but let me just quickly do a cargo run right here and now you can see that it did return the path to Powershell back to me all right so let's use spawn now instead so I'm going to create another process down here and let's say let P2 equal then we'll do command new and this time I'm going to launch Powershell so we'll just do pwsh and then we'll do P2 do args and this time I'm going to specify multiple arguments so in bash and Powershell you can pass a command that you want to run just as a oneoff command rather than passing in a script file path on the file system and in Powershell it's Dash command like so in bash I think it's just- C and then we can specify some output that we want from a command so we could say right host and then maybe do another argument here and say hello from pwsh or maybe we'll spell it out hello from Powershell okay so let's close that off with a semicolon and we also need to make P2 mutable because we're attempting to alter its state with this AR function right here and now we're going to do P2 dos spawn instead of using output and this will kick off our process a synchronously and give us a handle to it so let's do let P2 proc again you can call this whatever you want to I'm just going to call it P2 proc because it's the actual handle maybe we'll call it P2 handle and then you're going to see we get this result object so we could either have an error occur while it tries to spawn it like maybe a permissions related error where our user account doesn't have permission to that binary somewhere on the file system but if it is successful in kicking it off it'll give us back this child reference right here and that'll be a reference to the child process and that we can track so I'm just going to say dot unwrap right here we're going to assume that it's going to be successful spawning Powershell and then we can do a print line and just inspect the handle so this time we're going to have this child reference here if we head back over to the process module documentation take a look at the data structures here you can see we've got this child data structure and we have references to standard in standard out and Standard air so while a process is running you can actually send data into that process you can pipe data into that process's standard input Handle by grabbing a reference to standard in here and you also have to implement the right trait so I think that's under process here um it's actually probably under standard IO so let's just do a quick search for right here and yeah I think it's this one right here standard IO WR you can basically implement or U reference this trait in your program with a use statement and that'll give you access to the write or write all functions here so you can actually write data into a running program we'll take a look at how to do that in a moment but for now we just want to deal with the child handle itself so we'll do P2 do handle and we can check out Standard air standard in standard out and we can I think we can actually see if it's finished or not as well so let's head back over to process here take a look at the child type once again and we have the weight function here so basically this allows us to just wait until it's finished and then we can grab the exit status so let's do do print line and say doing some more work okay and so this statement right here on line number 21 is just indicating that we're running additional rust code while this background process or this child process is still executing separately right so we'll just immediately print out doing some more work and then after that we'll say process. weight and assign that to a variable so we'll say let Croc results equal weight and we'll unwrap that as well because that gives us a result wrapper here so we need to unwrap the exit status and then we can say exited with code and we can go to that exit status that we've already seen and grab the code function and unwrap it because it's an option type here and that'll allow us to retrieve that all right we also need to make this mutable here so P2 handle needs to be a mutable reference to the child process when we call the weight function here so let's turn that into mutable on line number 19 so now we should be able to kick off Powershell from rust and do cargo run you can see it says doing some more work and it pauses for just a moment while Powershell launches and the first time that Powershell runs it takes a little bit longer because the net runtime has to do a little bit of background just in time compilation but the next time you run it it should be quite a bit faster so as you can see we just get an exit code of zero and the standard output from the program is actually being written out right here as well now if you ever want to suppress the output from the program from being printed directly into your rust program and you just want your rust program to kind of control what's being outputed then what you can do is go back to your process configuration right here on this command object and say P2 do standard out and then for the configuration right here what what we can do is say standard process so we're going back into the process module and going into the standard IO type here and then we can specify a null exit point and so what that'll do is it'll redirect standard output from the program that's running and it'll just point it to null so that the output never actually gets printed to your terminal so now if we do cargo run once again you can see that that hello from Powershell has been suppressed and now rust is just taking care of running the program and returning the result all right so that's how we can accomplish that uh one other thing that we can do though is that we can actually customize environment variables and the reason that I introduced Powershell here is because it's really easy to just print out an environment variable from a interactive Powershell command here so let's say that we wanted to add an environment variable to our program here and instead of printing out the environment variable from rust we actually want to print it out from this Powershell child process that's being spawned from rust and so what we can do here is say Posh command and then say write host and then do a dollar EnV reference this is how you just reference an existing environment variable in Powershell and we'll just say dollar EnV colon first name so first name should be an environment variable and then Powershell should be able to write it out to our terminal and the way that we can accomplish that is by doing P2 Dov and we can then specify a key which is the environment variable name and a value that we want to assign to that environment variable so what we'll do is say first underscore name and set it to Trevor and we could also add other environment variables too also if you've already configured some environment variables and for some reason you want to clear them you can call EnV clear and that'll clear all explicitly set environment variables so that's a nice option as well but let's go ahead and just do this for now so now if we run our program down here let's also comment out line number 20 so that the standard output will get printed out it's not going to get redirected to null because we actually want to see in this case what Powershell is printing out and as you can see Powershell prints out Trevor right down here because it was able to retrieve that environment variable so that's a really cool way that you can specify environment variables also there's p2v remove so if you want to remove an environment variable with a key like first name we could go ahead and just remove that from the configuration maybe we've got five or six different variables and we just want to remove one of them you know we could certainly do that as well and then if you want to inspect the environment variables that are currently configured on the process then you can call envs here and this will actually allow you to insert multiple here I think what we wanted was get envs and get envs is going to return an iterator over the environment variables that are configured for the child process so if you need to inspect all of the configured variables you can just run that function and then do a iter iterator function and I've got a separate video that talks about iterators in Rust and how you can use those to kind of inspect and mutate different values inside of any kind of iterator so that's how you can get a reference to the environment variable iterators all right now I think the last thing that I wanted to cover was how to pass some standard input into a program and just to give you a little bit of context for what we're going to do here I'm going to start by doing something like ECHO line one and then say sln and then line two and if I do this with Echo D lowercase e I think and then let me put double quotes around this as well we should have line one and then a new line character and then line two now in bash normally if you do Echo with two lines and then you pipe into head- N1 then you can get only the first line or you could say head- N2 and get the first two lines of output so the whole point of the head command is to just grab the first number of lines from the output here and so what we're going to do is instead of piping the data in from echo in a bash shell we're actually going to configure the head command in Rust and then we're going to send some input as our standard in handle using rust instead of bash so down here let's just go ahead and move this all into a separate function for now we'll say FN test procs and put that down here and then I'll create another function called test standard in and and inside of this function we'll call from Main and what we're going to do down here is create another command and we're going to make the command head so we'll say let mute head CMD equal that and then we're going to add the argument of dn1 so we'll say head CMD do ARG and we'll add dn1 all right so now that we've got the command itself configured we need to go ahead and get a handle to standard input and we also need to be able to send some data into it right so the data that we want to feed into it is going to be a value just a static argument here so we'll say let input data you can call that whatever you want to we'll say input one sln input 2 so now we just have a string slice right here that's going to point to our string on the stack and we'll use that as our input to the program and just like standard out gives us a VEC of u8 we also use a slice of unsigned 8bit integers as input so we're not technically going to be sending the literal string here we actually need to take the string and turn it into a slice of unsigned 8bit integers because that's how the process module expects the input to a process so now that we've got the process configured here we are going to asynchronously spawn the process and this is really important because in order to pass standard in we have to spawn it asynchronously otherwise we won't be able to get a handle to the standard input stream if we use the output function which is a synchronous process as we saw before right down here then that is not going to give us the handle to standard input so what we're going to do is say head CMD do spawn and then we're going to assign the result of this to our process handle so we'll say proc handle equals the result of that and then we'll again assume that it's going to be successful here so we'll just say unwrap and then proc handle here should be a child process and again if we look at the documentation for that type in the process module here so let's go to process oh I was already actually on child here so then we'll have access to standard in standard out and Standard air so we need to get a reference to standard in right here and so what we're going to say is let standard in handle equal proc handle. standardin and then we need to write data to that and it's also in an option so we'll do unwrap there and so now we should just have a reference to child standard in now if we take a look at the docs for child standard in you're going to see a bunch of trait implementations here and one of those trait implementations is the right trait and this right trait we saw a little bit earlier points to standard IO right now if we import this trait in a use statement so we'll say use standard IO WR now the functions that are declared by that particular trait will be visible on our child standardin type that we get back from the spawn function so what we're going to do is take that standard in handle and we're going to write our data or input data on line number 12 right here into that call so we say standard in handle Dot and then write all and this takes a mutable buffer as input and so this is a borrowed string slice actually it's a borrowed slice of unsigned 8bit integers correction and so what we need to do is take our input data here and we need to turn it into a string or a unsigned 8bit integer slice so what I'll do is just say as bytes right here and that should turn it into a a string slice but it also needs to be mutable so I think can I turn that into mutable no I think what I need to do is say input data dot as mute nope do asbes or what is it I figured this out a little bit earlier and I can't remember exactly what I did let me take a quick look at that all right so what I didn't realize here is that it was actually complaining about mutation on the handle itself not the input data cuz we don't need the input data to be mutable so what I'm going to do is just call as byes right here on this string and that'll turn input data from a string to a borrowed slice of unsigned 8bit integers right so now we have an unsigned 8bit integer slice that represents this string right here so what we can do is take input data and just pass it directly in there because it's already a slice of unsigned apit integers but the message right here is saying it can't mutate the immutable variable standard in handle so this variable on line 15 is the one we need to be mutating all right so now that fixes the function signature here for write all and that's going to write the data as standard into the head Command right up here and so now all we need to do is grab the result so let's do grab result and we'll go ahead and save that and then we're also going to grab a reference to the standard output handle so we'll say proc handle. standard out and then we'll be able to read the output from standard out a little bit later on so let's do standard out handle equals that and then we're going to grab that after it's finished we'll say proc handle. weight and then after that we also need to make proc handle a mutable variable here as well and and so now it's complaining about a move occurring here and the partial move is occurring because we are grabbing standard out here so we're going to have to kind of switch around exactly how this is working all right so let's go ahead and do that after the weight call here and the way that we can work around this borrow issue right here is actually to go onto the standard in handle right here and then call take on it and that should resolve our issue with borrowing it there and then we'll just go ahead and discard this result there by doing an underscore on the left hand side and we'll actually do the same thing right here as well because we don't need to capture that result from the standard input right operation but then what we can do is WR down here just grab standard out and then turn that into a string value so we'll do string from utf8 and we'll go ahead and pass in our result from our standard output here and then what we're going to do is we also need to specify that we want to pipe in data up here so one of the things that I forgot to do on the actual command configuration is to specify that we're going to do piped input so let's do head CMD Dot and then we want to do standard in and then we need to specify standard process standard IO and then this time instead of using null down here we're going to use piped instead and that will indicate that we're going to be piping data into standard input all right so let's check out what's going on down here with our result so let's do standard out result instead of handle here and we're going to do string from utf8 we're going to grab standard out and then we're going to grab we're going to unwrap W that and let's see what else we need to do to get our VEC back here so then we'll do dot unwrap Dot and let me see what's up here all right so there's actually an easier way to handle this than I thought so what we're going to do is grab proc handle do standard out and then we are going to grab another trait called the read trait so if we take a look at the child standard out here and go to read this is a trait that's going to allow us to call read to string and this is a nice helper here that'll give us the ability to turn it directly into a string instead so we're going to go ahead and use that so let's do use standard IO and then read right here and that should give us access to those helper functions so on standard out we should be able to unwrap that and then do a read to string operation and we also need to provide it an input buffer here so let's say let output buffer equal string new and then we can pass in that output buffer here and provide a mutable reference to it and it's it's not declared as mutable so let's make it mutable here as well so now right over here we should have a string value and it's going to return a u size indicating the length of the bytes that were read I believe from it but the buffer here that's a string should actually be populated with the final result so we'll do a print line and say result was and then we'll pass in the output buffer string so now now let's go ahead and do a cargo run here and so we've got input one right here and it's complaining that we are calling unwrap on a none value here so this is on line 22 and so this is probably happening because standard out should provide the read to string capability here if it's available so for some reason standard out here is returning none instead of an actual value in its option here and I think the reason that's probably happening is because the output is actually directly getting printed to our terminal here so normally if you want to directly manipulate standard output we'd actually have to make it piped just like we did for standard input right here so what we'll do is turn it from the standard output to piped so we'll do head CMD standard out and then do standard process standard IO and then turn it into piped instead of having it directly printed out and I think it's getting printed out when we actually call the weight function here so when we actually pause execution of the process until the child process is completed I think that that point that's where it's kind of getting printed out and then standard out right down here on line 23 now is res returning a nun value and we were getting a panic because we were trying to call unwrap on a none value that's normally why you'd have error handling in place but I know what's going on here I need to turn it into piped output so if we rerun the cargo run you can now see that result was input one and then if I just change this program a little bit to grab the first two lines instead so head- N2 we should see both line one and two coming from this when the process exits but I think it's getting a little bit hung up on the new line character so we're not going to see that there but in any case this is at least in theory how this should work we could also try commenting out the standard out here and then just avoid manually manipulating standard output and populating that buffer and so then if we just resort to using our standard standard output mechanism where we call weight to pause execution on that child process we should see both of those inputs here and I'm not sure why it's not cooperating here but uh you know in theory this should be working here where it's printing out both lines I just looked at my other code sample that I had this working in before I started recording this video and for some reason I wasn't actually calling the weight function in my sample code all I was doing was calling right all here and sure enough when I do that it does end up putting out input one and input two here so something when I call weight is is actually getting hung up on the head program here I probably need to do something like close the standard input handle or something like that but at least you can see here that when we send in this input data here and change the number of lines that we want from the head program here it does change the number of lines that we get in our output down here so in any case I hope that you learned something new from this video I know things were a little bit bizarre here with the kind of manual approach to doing standard input piping but hopefully you kind of get the idea how we can Define some input turn it into a bite VC and then we can just feed that into the standard input handle as long as we configure our process to take piped input for its standard in handle so in any case thanks for watching this video again please provide some support to this channel like the video subscribe to the channel and also go to the pinned link down below which is my Amazon storefront use those links to purchase some common household products there's a whole bunch of stuff in there that I think you'll find interesting and if you have other suggestions for things to add there please also leave a comment on that down below thanks for watching and we'll see you in the next video take care
Info
Channel: Trevor Sullivan
Views: 4,344
Rating: undefined out of 5
Keywords: rust, rustlang, rust developer, rust programming, rust software, software, open source software, systems programming, data structures, rust structs, rust enums, rust coding, rust development, rustlang tutorial, rust videos, rust programming tutorial, getting started with rust, beginner with rust programming, rust concepts
Id: RtVzlk4om6U
Channel Id: undefined
Length: 54min 6sec (3246 seconds)
Published: Tue Sep 26 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.