1) Tuple Unpacking + Tuple Unpacking With *
person = ['bob', 30, 'male']
name, age, gender = person
# name='bob, age=30, gender='male'
2) List Comprehension + Dict/Set Comprehension
lis = [expression for i in iterable if condition]
l1 = [i for i in range(1,4)] # [1,2,3]
l2 = [i*2 for i in range(1,4)] # [2,4,6]
l3 = [i**2 for i in range(1,4)] # [1,4,9]
l4 = [i for i in range(1,4) if i%2==1] # [1,3]
^ with list comprehension, we can create a custom list in one line of code.
set1 = {i for i in range(1,4)} # {1,2,3}
d1 = {i:i**2 for i in range(1,4)} # {1:1, 2:4, 3:9}
3) Ternary operator
score = 57
if score > 90:
grade = 'A*'
elif score > 50:
grade = 'pass'
else:
grade = 'fail'
# grade = 'pass'
^ a normal if-elif-else block
score = 57
grade = 'A*' if score>90 else 'pass' if score>50 else 'fail'
# grade = 'pass'
^ we can condense the if-elif-else block into ONE line using the ternary operator.
4) Magic Methods In Python Classes
class Dog():
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'Dog(name={self.name}, age={self.age})'
def __gt__(self, otherDog):
return self.age > otherDog.age
^ apart from __init__, __str__ and __gt__ are magic methods that allow us to do special things with our Dog objects.
dog = Dog('rocky', 4)
print(dog) # Dog(name=rocky, age=4)
^ the __str__ magic method defines what is returned when we call str(dog), which is called when we print the dog object.
dog1 = Dog('rocky', 4)
dog2 = Dog('fifi', 2)
print(dog1 > dog2) # True
^ the __gt__ magic method defines what happens when we compare 2 dogs using the > operator.
5) *args and **kwargs
def test(a, b, *args):
print(f'{a=} {b=} {args=}')
test(1,2,3,4,5) # a=1 b=2 args=(3,4,5)
^ *args allow our functions to take in any number of positional arguments (which will be stored in a tuple args)
def test(a, b, **kwargs):
print(f'{a=} {b=} {kwargs=}')
test(a=1, b=2, c=3, d=4) # a=1 b=2 kwargs={'c': 3, 'd': 4}
^ **kwargs allow our functions to take in any number of keyword arguments (which will be stored in a dict kwargs)
6) Working with multiple .py files
# helper.py
def test123():
print('test123 is called')
# main.py
from helper import test123
test123() # test123 is called
When you get a job as a software engineer, you WILL work on projects with many many many different files. Do get familiar early with how to import functions from other files.
7) if __name__ == ‘__main__’
# helper.py
def test123():
print('test123 is called')
if __name__ == '__main__':
# this line only runs if we run helper.py DIRECTLY
print('print statement from helper.py')
# main.py
from helper import *
test123() # test123 is called
^ the line if __name__ == '__main__' evaluates to True in a .py file only if we run the .py file directly. We use this line so that we don’t accidentally run lines of code that we don’t intend to run.
8) Truthy & falsy values
# 0 if falsy, and evaluates to False
if 0: print('this wont print')
# non-zero numbers are truthy, and evaluate to True
if 1: print('this prints')
if 2: print('this prints')
if 100: print('this prints')
if -1: print('this prints')
if 3.14: print('this prints')
# empty sequences are falsy, and evaluate to False
if '': print('this wont print')
if []: print('this wont print')
if {}: print('this wont print')
if set(): print('this wont print')
# non-empty sequences are truthy, and evaluate to True
if 'a': print('this prints')
if [1]: print('this prints')
if {2:3}: print('this prints')
if {1,2}: print('this prints')
# None is falsy, and evaluates to False
obj = None
if obj: print('this wont print')
# objects are truthy, and evaluates to True
obj = Dog()
if obj: print('this prints')
9) break vs continue vs pass
for i in [1,2,3,4,5]:
if i == 3:
break
print(i)
# this prints 1 and 2
^ break stops the for/while loop entirely. No other iteration happens.
for i in [1,2,3,4,5]:
if i == 3:
continue
print(i)
# this prints 1, 2, 4 and 5
^ continue skips ONE iteration. Other iterations still happen afterwards.
for i in [1,2,3,4,5]:
if i == 3:
pass
print(i)
# this prints 1, 2, 3, 4 and 5
^ pass does absolutely nothing.
10) try except finally blocks
try:
# risky code that could cause exceptions
except:
# this block executes if an exception happens in the try block
finally:
# stuff here will ALWAYS execute
^ try-except-finally blocks allow us to handle stuff when errors and exceptions happen in our code (instead of just crashing)
11) Python Web API building libraries
If you intend to be a software engineer, chances are that you need to know this quite well. I learnt about this rather late, and wish I was exposed to this much earlier in my coding journey.
Some easy-to-learn libraries in Python:
Python FastAPI — this allows us to build APIs very easily
Python Flask — we can build APIs using Flask too, and even simple web applications
12) Decorators
I learnt about this VERY late in my Python journey, and used to ignore any @decorator syntax that I saw. But it’s better to understand what’s happening in your code.
def add_exclamation_mark(your_function):
def inner(*args, **kwargs):
return your_function(*args, **kwargs)
return inner
@add_exclamation_mark
def greet(name):
return f'hello {name}'
Decorators are functions that 1) take in another function 2) tweak how the functio works and 3) return another function. When we put @add_exclamation_mark above the greet function, we are actually decorating the greet function, and changing how the greet function works.
# @add_exclamation_mark
# def greet(name)
#
# ^ THIS IS THE SAME AS BELOW:
#
# greet = add_exclamation_mark(greet)
print(greet('tim')) # hello tim!
^ what happens when we call the decorated greet function. Due to our decorator, our greet function behaves differently, and now has an additional ! after its returned value.
13) Generators + the ‘yield’ Keyword
def simple_generator():
yield 'apple'
yield 'orange'
yield 'pear'
for fruit in simple_generator():
print(fruit)
# apple orange pear
The yield keyword is like the return keyword. Except that the function does not stop completely after yielding something.
A function that contains the yield keyword becomes a generator function, and can have multiple outputs (the stuff that are yielded).
14) Method chaining
I actually learnt about this much later than I should have.
s = ' APPLE ORANGE PEAR '
s = s.strip() # s is now 'APPLE ORANGE PEAR'
s = s.lower() # s is now 'apple orange pear'
s = s.split() # s is now ['apple', 'orange', 'pear']
^ some generic code to clean a string.
s = ' APPLE ORANGE PEAR '
s = s.strip().lower().split()
# s is now ['apple', 'orange', 'pear']
^ we can chain multiple methods together in one line to save ourselves a few lines of code.
15) Basic machine learning — regression & classification
Before I knew (on a basic level) how machine learning worked, I thought it was some sort of magical magic.
After I learnt how machine learning worked (on a basic level), I was like oh ok so it’s kinda automated statistics with help from a computer. It was no longer magical magic.
Machine learning is a huge huge field, but we usually start with supervised learning — more specifically, classification and regression. As a starter kit, do check out scikit-learn, a Python library that does all the machine learning code for you, and allows you to simply use their functions and classes.
16) Basic Data Structures & Algorithms
After going through countless coding interviews for internships and full-time positions, I realised how important this step is. Most if not all of the coding interviews required the interviewee to be decently competent in this area.
I have unfortunately screwed up quite a few coding interviews at big-name companies just because I didn’t practice enough, which probably cost me quite a few excellent internships.
If you’re interviewing soon or currently, and think that you can wing the interview because you’re good, DON’T. PRACTICE your data structures and algorithms. Take it from someone who made the same mistake years ago.
The earlier you start practicing these coding interview questions, the better you get at them, and the better the opportunities you get.
17) Different data structures & when to use them
Python has a couple of built-in data structures
# ordered collection of elements
list1 = [1,2,3]
# an immutable list. we can use this as a dict key
tuple1 = (1,2,3)
# O(1) when accessing a value using a key
dict1 = {'apple':4, 'orange':5}
# unordered collection containing only unique elements
# O(1) when checking if element exists inside a set
set1 = {1,2,3}
# an immutable set. we can use this as a dict key
frozenset1 = frozenset({1,2,3})
18) Lambda functions
For a long time, I saw a lambda function and went ok I’m gonna ignore that. Until I actually took some time to understand them. And found out how simple it actually was.
def add(x, y):
return x + y
# this is the same as
add = lambda x,y : x + y
^ lambda functions are simply normal functions, but written using a different syntax. More examples:
def test():
return 'hello
# this is the same as
test = lambda : 'hello'
def test2(a,b,c,d):
return (a+b) / (c-d)
# this is the same as
test2 = lambda a,b,c,d : (a+b) / (c-d)
19) assert + raise + custom exceptions
assert score <= 100
# ensuring that score cannot be above 100.
^ the assert keyword allows us to conduct a sanity test in the middle of our code. If score > 100, an AssertionError is raised, and our program crashes forcefully.
if score > 100:
raise Exception('score cannot be higher than 100')
# ensuring that score cannot be above 100.
^ the raise keyword allows us to forcefully cause an Exception (we can customize the message in the Exeption too)
class ScoreException(Exception):
def __init__(self):
super().__init__('score cannot be higher than 100')
if score > 100:
raise ScoreException()
^ we can also create our own Exception types by inheriting from the Exception class.
20) Multiprocessing in Python
The built-in multiprocessing module in Python allows us to run more than 1 function concurrently (at the same time).
import multiprocessing
import time
import datetime
def yourfunction(x):
start = datetime.datetime.now()
time.sleep(1)
end = datetime.datetime.now()
return f'x={x} start at {start}, end at {end}'
if __name__ == '__main__':
with multiprocessing.Pool(processes=3) as pool:
data = pool.map(yourfunction, [1, 2, 3, 4, 5, 6, 7])
for row in data:
print(row)
x=1 start at 2023-04-16 13:39:32.035510, end at 2023-04-16 13:39:33.037308
x=2 start at 2023-04-16 13:39:32.035795, end at 2023-04-16 13:39:33.037324
x=3 start at 2023-04-16 13:39:32.037349, end at 2023-04-16 13:39:33.037629
x=4 start at 2023-04-16 13:39:33.037725, end at 2023-04-16 13:39:34.040135
x=5 start at 2023-04-16 13:39:33.037892, end at 2023-04-16 13:39:34.040160
x=6 start at 2023-04-16 13:39:33.037986, end at 2023-04-16 13:39:34.040161
x=7 start at 2023-04-16 13:39:34.040454, end at 2023-04-16 13:39:35.045383
Here, my code runs 3 functions concurrently (each by 1 worker)
yourfunction(1) yourfunction(2) & yourfunction(3) runs at the same time.
yourfunction(4) yourfunction(5) & yourfunction(6) run at the same time also.
yourfunction(7) runs on its own
If you don’t like change, data engineering is not for you. Little in this space has escaped reinvention.
The most prominent, recent examples are Snowflake and Databricks disrupting the concept of the database and ushering in the modern data stack era.
As part of this movement, Fivetran and dbt fundamentally altered the data pipeline from ETL to ELT. Hightouch interrupted SaaS eating the world in an attempt to shift the center of gravity to the data warehouse. Monte Carlo joined the fray and said, “Maybe having engineers manually code unit tests isn’t the best way to ensure data quality.”
Today, data engineers continue to stomp on hard coded pipelines and on-premises servers as they march up the modern data stack slope of enlightenment. The inevitable consolidation and trough of disillusionment appear at a safe distance on the horizon.
And so it almost seems unfair that new ideas are already springing up to disrupt the disruptors:
Zero-ETL has data ingestion in its sights
AI and Large Language Models could transform transformation
Data product containers are eyeing the table’s thrown as the core building block of data
Are we going to have to rebuild everything (again)? The body of the Hadoop era isn’t even all that cold.
The answer is, yes of course we will have to rebuild our data systems. Probably several times throughout our careers. The real questions are the why, when, and the how (in that order).
I don’t profess to have all the answers or a crystal ball. But this article will closely examine some of the most prominent near(ish) future ideas that may become part of the post-modern data stack as well as their potential impact on data engineering.
Practicalities and tradeoffs
The modern data stack didn’t arise because it did everything better than its predecessor. There are real trade-offs. Data is bigger and faster, but it’s also messier and less governed. The jury is still out on cost efficiency.
The modern data stack reigns supreme because it supports use cases and unlocks value from data in ways that were previously, if not impossible, then certainly very difficult. Machine learning moved from buzz word to revenue generator. Analytics and experimentation can go deeper to support bigger decisions.
The same will be true for each of the trends below. There will be pros and cons, but what will drive adoption is how they, or the dark horse idea we haven’t yet discovered, unlock new ways to leverage data. Let’s look closer at each.
Zero-ETL
What it is: A misnomer for one thing; the data pipeline still exists.
Today, data is often generated by a service and written into a transactional database. An automatic pipeline is deployed which not only moves the raw data to the analytical data warehouse, but modifies it slightly along the way.
For example, APIs will export data in JSON format and the ingestion pipeline will need to not only transport the data but apply light transformation to ensure it is in a table format that can be loaded into the data warehouse. Other common light transformations done within the ingestion phase are data formatting and deduplication.
While you can do heavier transformations by hard coding pipelines in Python, and some have advocated for doing just that to deliver data pre-modeled to the warehouse, most data teams choose not to do so for expediency and visibility/quality reasons.
Zero-ETL changes this ingestion process by having the transactional database do the data cleaning and normalization prior to automatically loading it into the data warehouse. It’s important to note the data is still in a relatively raw state.
At the moment, this tight integration is possible because most zero-ETL architectures require both the transactional database and data warehouse to be from the same cloud provider.
Pros: Reduced latency. No duplicate data storage. One less source for failure.
Cons: Less ability to customize how the data is treated during the ingestion phase. Some vendor lock-in.
Who’s driving it: AWS is the driver behind the buzzword (Aurora to Redshift), but GCP (BigTable to BigQuery) and Snowflake (Unistore) all offer similar capabilities. Snowflake (Secure Data Sharing) and Databricks (Delta Sharing) are also pursuing what they call “no copy data sharing.” This process actually doesn’t involve ETL and instead provides expanded access to the data where it’s stored.
Practicality and value unlock potential: On one hand, with the tech giants behind it and ready to go capabilities, zero-ETL seems like it’s only a matter of time. On the other, I’ve observed data teams decoupling rather than more tightly integrating their operational and analytical databases to prevent unexpected schema changes from crashing the entire operation.
This innovation could further lower the visibility and accountability of software engineers toward the data their services produce. Why should they care about the schema when the data is already on its way to the warehouse shortly after the code is committed?
With data steaming and micro-batch approaches seeming to serve most demands for “real-time” data at the moment, I see the primary business driver for this type of innovation as infrastructure simplification. And while that’s nothing to scoff at, the possibility for no copy data sharing to remove obstacles to lengthy security reviews may result in greater adoption in the long-run (although to be clear it’s not an either/or).
One Big Table and Large Language Models
What it is: Currently, business stakeholders need to express their requirements, metrics, and logic to data professionals who then translate it all into a SQL query and maybe even a dashboard. That process takes time, even when all the data already exists within the data warehouse. Not to mention on the data team’s list of favorite activities, ad-hoc data requests rank somewhere between root canal and documentation.
There is a bevy of startups aiming to take the power of large language models like GPT-4 to automate that process by letting consumers “query” the data in their natural language in a slick interface.
At least until our new robot overlords make Binary the new official language.
This would radically simplify the self-service analytics process and further democratize data, but it will be difficult to solve beyond basic “metric fetching,” given the complexity of data pipelines for more advanced analytics.
But what if that complexity was simplified by stuffing all the raw data into one big table?
That was the idea put forth by Benn Stancil, one of data’s best and forward thinking writer/founders. No one has imagined the death of the modern data stack more.
As a concept, it’s not that far-fetched. Some data teams already leverage a one big table (OBT) strategy, which has both proponents and detractors.
Leveraging large language models would seem to overcome one of the biggest challenges of using the one big table, which is the difficulty of discovery, pattern recognition, and its complete lack of organization. It’s helpful for humans to have a table of contents and well marked chapters for their story, but AI doesn’t care.
Pros: Perhaps, finally delivering on the promise of self service data analytics. Speed to insights. Enables the data team to spend more time unlocking data value and building, and less time responding to ad-hoc queries.
Cons: Is it too much freedom? Data professionals are familiar with the painful eccentricities of data (time zones! What is an “account?”) to an extent most business stakeholders are not. Do we benefit from having a representational rather than direct data democracy?
Who’s driving it: Super early startups such as Delphi and GetDot.AI. Startups such as Narrator. More established players doing some version of this such as AWS QuickSite, Tableau Ask Data, or ThoughtSpot.
Practicality and value unlock potential: Refreshingly, this is not a technology in search of a use case. The value and efficiencies are evident–but so are the technical challenges. This vision is still being built and will need more time to develop. Perhaps the biggest obstacle to adoption will be the infrastructure disruption required, which will likely be too risky for more established organizations.
Data product containers
What it is: A data table is the building block of data from which data products are built. In fact, many data leaders consider production tables to be their data products. However, for a data table to be treated like a product a lot of functionality needs to be layered on including access management, discovery, and data reliability.
Containerization has been integral to the microservices movement in software engineering. They enhance portability, infrastructure abstraction, and ultimately enable organizations to scale microservices. The data product container concept imagines a similar containerization of the data table.
Data product containers may prove to be an effective mechanism for making data much more reliable and governable, especially if they can better surface information such as the semantic definition, data lineage, and quality metrics associated with the underlying unit of data.
Pros: Data product containers look to be a way to better package and execute on the four data mesh principles (federated governance, data self service, treating data like a product, domain first infrastructure).
Cons: Will this concept make it easier or more difficult for organizations to scale their data products? Another fundamental question, which could be asked of many of these futuristic data trends, is do the byproducts of data pipelines (code, data, metadata) contain value for data teams that is worth preserving?
Who’s driving it: Nextdata, the startup founded by data mesh creator Zhamak Dehgahni. Nexla has been playing in this space as well.
Practicality and value unlock potential: While Nextdata has only recently emerged from stealth and data product containers are still evolving, many data teams have seen proven results from data mesh implementations. The future of the data table will be dependent on the exact shape and execution of these containers.
The endless reimagination of the data lifecycle
Photo by zero take on Unsplash
To peer into the data future, we need to look over our shoulder at the data past and present. Past, present, future — data infrastructures are in a constant state of disruption and rebirth (although perhaps we need some more chaos).
The meaning of data warehouse has changed drastically from the term introduced by Bill Inmon in the 1990s. ETL pipelines are now ELT pipelines. The data lake is not as amorphous as it was just two years ago.
With these innovations ushered in by the modern data stack, data engineers have still played a central, technical role in dictating how data moves and how data consumers access it. But some changes are bigger and scarier than others.
The term zero-ETL seems threatening because it (inaccurately) suggests the death of pipelines, and without pipelines do we need data engineers?
For all the hype behind ChatGPT’s ability to generate code, this process is still very much in the hands of technical data engineers who still need to review and debug. The scary aspect about large language models is how they might fundamentally warp the data pipeline or our relationship with data consumers (and how data is served to them).
However this future, if and when it comes to pass, is still strongly reliant on data engineers.
What has endured since the dawn of time is the general lifecycle of data. It is emitted, it is shaped, it is used, and then it is archived (best to avoid dwelling on our own mortality here).
While the underlying infrastructure may change and automations will shift time and attention to the right or left, human data engineers will continue to play a crucial role in extracting value from data for the foreseeable future.
It’s not because future technologies and innovations can’t simplify the complex data infrastructures of today–it’s because our demand and uses for data will continue to increase in sophistication and scale.
Big data has and always will be a pendulum swinging back and forth. We make a leap forward in capacity and then we just as quickly figure out a way to reach those boundaries until the next leap is required. Take comfort in this cycle–it’s nice to be needed.
Shane Murray co-authored this article. Subscribe to get his stories delivered to your inbox.
Interested in the future of data quality? Reach out to the Monte Carlo Team!
Fluency is the ability to “[make] the best use of what is already known” (Nation, 2014:11).
To become more fluent, you don’t need to learn more language. You need to practice using the English that you have already learned, no matter how limited it might be. I explained this in my previous article.
But how exactly can you do this?
In this article, I’ll show you a simple fluency activity called “4–3–2” and explain how it can help you improve your speaking fluency.
It’s easy, it’s short and you can even do it every day on your own.
The original version of “4–3–2” should be done in a group of 4 people, so you would need to have 3 friends who would like to do it with you.
If you don’t have 3 friends who are learning English, no worries, you can adapt “4–3–2” so you can do it by yourself.
“4–3–2” explained
“4–3–2” is a speaking activity that requires you to give the same talk to a different person with less time to do it each time.
There are four steps.
Step 1: Choose a topic you can talk about without too much difficulty and spend a few minutes preparing a talk on this topic without making notes.
Step 2: Set a time limit of 4 minutes, press start, and give a 4-minute talk about the topic to one of your friends. Your friend must not speak, interact or ask questions. She will give her talk to you after you’ve finished giving yours.
Step 3: Once the time is up, stop talking, change partner, and give the same talk. But this time you have 3 minutes to give the same amount of information.
Step 4: After 3 minutes, stop, change partner again, set the timer to 2 minutes, and give the same talk to your third friend. After 2 minutes, stop talking.
End of the activity.
If you don’t have 3 friends who are learning English, no problem. Keep reducing the time available (4 minutes, then 3, then 2) but talk to yourself.
Is this really an activity that can help you improve your fluency?
Yes, it is.
Linguists and researchers tested it many times and found that it works (Thai & Boers, 2016; De Jong & Perfetti, 2011; Ghasemi & Mozaheb, 2021).
I’ve used it many times with my learners too and it has never failed.
There are 4 reasons why it’s effective.
4 reasons why “4–3–2” is an effective fluency activity
After years and years of research into second language acquisition, Paul Nation, an internationally recognized scholar in the field of linguistics and teaching methodology — a man much smarter than me — found that if you want to become more fluent, you need to do speaking activities that require you to:
use language you’re familiar with.
focus on giving the message, not practicing specific grammatical structures or vocabulary.
speak at a faster than usual speed.
produce a large amount of output.
(Nation, 2007)
“4-3-2” meets all these requirements.
1. You use language you’re largely familiar with because you choose a topic that’s not too difficult for you to talk about.
The first time you give the talk you might need time to think about what words to use and how to structure your sentences, so your speech may not be fluent.
The second time you do it, however, you have a better idea about what to say and how. Giving the talk becomes easier so you’ll sound more confident — and fluent.
The third time it gets even better.
2. You will focus your attention on the message. You’re not giving a talk to practice the past simple, the present continuous, or vocabulary around holidays and travel.
Your attention is directed more toward meaning than form.
And if you do “4–3–2” in a group, you have a different audience every time you repeat your talk so you will want your friend to get the entire message as clearly as possible before the time is up.
3. You’ll speak at a faster than usual speed. Reducing the time will put you under pressure. You can’t waste time on thinking about grammar and words. You need to get to the end of the talk before the timer beeps!
4. You’ll produce a large amount of output. You’ll be speaking a lot: 4+3+2=9 minutes.
Final thoughts on “4–3–2”
“4/3/2” is a fluency activity you can do every day to improve. Ideally, you would do it in a group to get the most out of it, but you don’t have to.
It can become one of the self-study practice activities that you do to improve your English.
You can do it in under 15 minutes sitting on your couch and you just need a timer or a stopwatch to make it work.
For a more detailed and academic explanation of “4–3–2”, see this.
I hope this is helpful. Please feel free to ask me all the questions you want about “4–3–2”.
I’ll be happy to help you.
When I was young fishing at Michigan in early 80s. My first rod was a fiberglass fishing rod. For many years I used this fiberglass fishing rod. It never broke, never had any issues. I miss that fiberglass fishing rod. Finally KastKing created a light weight fiberglass baitcasting rod.
KastKing Brutus Glass Fiber CastingRods 7ft, Fast Action, Medium Heavy Power, 10-25 Line Weight, 1/4 - 3/4 OZ Lure Weight, 7+1 Guides, 2 Pieces, 8.45 OZ (239 grams) Weight
Brute Tuff Composite Glass/ Carbon Fiber Fishing Rods– It’s a Brute! KastKing Brutus fishing rods are designed for incredible toughness and durability without losing the performance and feel all anglers want. rute Tuff composite blanks are for extreme conditions, hard fighting fish, and anglers who challenge the elements to catch more and bigger fish. Each spinning and casting rod feature a chartreuse “Strike Tip” for early strike detection even in low light conditions.
#!/bin/bash
# mikeyom@gmail.com 04-2023
my_path=/var/lib/jenkins/mike_scripts/z_pub_ip
declare -a set1_list=("host1" "host2" "host3" "host4")
for my_set in "${set1_list[@]}"
do
echo "============================================================"
echo ""
echo $my_set
echo ""
gcloud compute instances --project $my_set list | grep RUNNING > $my_path/$my_set.txt
cat $my_path/$my_set.txt
done